CameraManager: Add physical camera availability callback
Add CameraManager callback for physical camera availability. Bug: 119325027 Test: Camera CTS Change-Id: Ibe0357f5034769511576cc71c04365a1009a2be1
This commit is contained in:
@@ -17220,6 +17220,8 @@ package android.hardware.camera2 {
|
||||
method public void onCameraAccessPrioritiesChanged();
|
||||
method public void onCameraAvailable(@NonNull String);
|
||||
method public void onCameraUnavailable(@NonNull String);
|
||||
method public void onPhysicalCameraAvailable(@NonNull String, @NonNull String);
|
||||
method public void onPhysicalCameraUnavailable(@NonNull String, @NonNull String);
|
||||
}
|
||||
|
||||
public abstract static class CameraManager.TorchCallback {
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.os.Parcelable;
|
||||
public class CameraStatus implements Parcelable {
|
||||
public String cameraId;
|
||||
public int status;
|
||||
public String[] unavailablePhysicalCameras;
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
@@ -40,11 +41,13 @@ public class CameraStatus implements Parcelable {
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(cameraId);
|
||||
out.writeInt(status);
|
||||
out.writeStringArray(unavailablePhysicalCameras);
|
||||
}
|
||||
|
||||
public void readFromParcel(Parcel in) {
|
||||
cameraId = in.readString();
|
||||
status = in.readInt();
|
||||
unavailablePhysicalCameras = in.readStringArray();
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<CameraStatus> CREATOR =
|
||||
|
||||
@@ -718,6 +718,52 @@ public final class CameraManager {
|
||||
public void onCameraAccessPrioritiesChanged() {
|
||||
// default empty implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* A physical camera has become available for use again.
|
||||
*
|
||||
* <p>By default, all of the physical cameras of a logical multi-camera are
|
||||
* available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
|
||||
* cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
|
||||
* multi-camera is invoked. However, if some specific physical cameras are unavailable
|
||||
* to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
|
||||
* {@link #onCameraAvailable}.</p>
|
||||
*
|
||||
* <p>The default implementation of this method does nothing.</p>
|
||||
*
|
||||
* @param cameraId The unique identifier of the logical multi-camera.
|
||||
* @param physicalCameraId The unique identifier of the physical camera.
|
||||
*
|
||||
* @see #onCameraAvailable
|
||||
* @see #onPhysicalCameraUnavailable
|
||||
*/
|
||||
public void onPhysicalCameraAvailable(@NonNull String cameraId,
|
||||
@NonNull String physicalCameraId) {
|
||||
// default empty implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* A previously-available physical camera has become unavailable for use.
|
||||
*
|
||||
* <p>By default, all of the physical cameras of a logical multi-camera are
|
||||
* available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
|
||||
* cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
|
||||
* multi-camera is invoked. If some specific physical cameras are unavailable
|
||||
* to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
|
||||
* {@link #onCameraAvailable}.</p>
|
||||
*
|
||||
* <p>The default implementation of this method does nothing.</p>
|
||||
*
|
||||
* @param cameraId The unique identifier of the logical multi-camera.
|
||||
* @param physicalCameraId The unique identifier of the physical camera.
|
||||
*
|
||||
* @see #onCameraAvailable
|
||||
* @see #onPhysicalCameraAvailable
|
||||
*/
|
||||
public void onPhysicalCameraUnavailable(@NonNull String cameraId,
|
||||
@NonNull String physicalCameraId) {
|
||||
// default empty implementation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -914,6 +960,9 @@ public final class CameraManager {
|
||||
private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
|
||||
// Camera ID -> Status map
|
||||
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
|
||||
// Camera ID -> (physical camera ID -> Status map)
|
||||
private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices =
|
||||
new ArrayMap<String, ArrayList<String>>();
|
||||
|
||||
// Registered availablility callbacks and their executors
|
||||
private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
|
||||
@@ -1003,6 +1052,14 @@ public final class CameraManager {
|
||||
CameraStatus[] cameraStatuses = cameraService.addListener(this);
|
||||
for (CameraStatus c : cameraStatuses) {
|
||||
onStatusChangedLocked(c.status, c.cameraId);
|
||||
|
||||
if (c.unavailablePhysicalCameras != null) {
|
||||
for (String unavailPhysicalCamera : c.unavailablePhysicalCameras) {
|
||||
onPhysicalCameraStatusChangedLocked(
|
||||
ICameraServiceListener.STATUS_NOT_PRESENT,
|
||||
c.cameraId, unavailPhysicalCamera);
|
||||
}
|
||||
}
|
||||
}
|
||||
mCameraService = cameraService;
|
||||
} catch(ServiceSpecificException e) {
|
||||
@@ -1086,6 +1143,10 @@ public final class CameraManager {
|
||||
public void onStatusChanged(int status, String id) throws RemoteException {
|
||||
}
|
||||
@Override
|
||||
public void onPhysicalCameraStatusChanged(int status,
|
||||
String id, String physicalId) throws RemoteException {
|
||||
}
|
||||
@Override
|
||||
public void onTorchStatusChanged(int status, String id) throws RemoteException {
|
||||
}
|
||||
@Override
|
||||
@@ -1236,7 +1297,7 @@ public final class CameraManager {
|
||||
}
|
||||
|
||||
private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
|
||||
final String id, final int status) {
|
||||
final String id, final String physicalId, final int status) {
|
||||
if (isAvailable(status)) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
@@ -1244,7 +1305,11 @@ public final class CameraManager {
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onCameraAvailable(id);
|
||||
if (physicalId == null) {
|
||||
callback.onCameraAvailable(id);
|
||||
} else {
|
||||
callback.onPhysicalCameraAvailable(id, physicalId);
|
||||
}
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
@@ -1257,7 +1322,11 @@ public final class CameraManager {
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onCameraUnavailable(id);
|
||||
if (physicalId == null) {
|
||||
callback.onCameraUnavailable(id);
|
||||
} else {
|
||||
callback.onPhysicalCameraUnavailable(id, physicalId);
|
||||
}
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
@@ -1304,7 +1373,16 @@ public final class CameraManager {
|
||||
for (int i = 0; i < mDeviceStatus.size(); i++) {
|
||||
String id = mDeviceStatus.keyAt(i);
|
||||
Integer status = mDeviceStatus.valueAt(i);
|
||||
postSingleUpdate(callback, executor, id, status);
|
||||
postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
|
||||
|
||||
// Send the NOT_PRESENT state for unavailable physical cameras
|
||||
if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) {
|
||||
ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id);
|
||||
for (String unavailableId : unavailableIds) {
|
||||
postSingleUpdate(callback, executor, id, unavailableId,
|
||||
ICameraServiceListener.STATUS_NOT_PRESENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1323,8 +1401,12 @@ public final class CameraManager {
|
||||
Integer oldStatus;
|
||||
if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
|
||||
oldStatus = mDeviceStatus.remove(id);
|
||||
mUnavailablePhysicalDevices.remove(id);
|
||||
} else {
|
||||
oldStatus = mDeviceStatus.put(id, status);
|
||||
if (oldStatus == null) {
|
||||
mUnavailablePhysicalDevices.put(id, new ArrayList<String>());
|
||||
}
|
||||
}
|
||||
|
||||
if (oldStatus != null && oldStatus == status) {
|
||||
@@ -1366,10 +1448,62 @@ public final class CameraManager {
|
||||
Executor executor = mCallbackMap.valueAt(i);
|
||||
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
|
||||
|
||||
postSingleUpdate(callback, executor, id, status);
|
||||
postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
|
||||
}
|
||||
} // onStatusChangedLocked
|
||||
|
||||
private void onPhysicalCameraStatusChangedLocked(int status,
|
||||
String id, String physicalId) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG,
|
||||
String.format("Camera id %s physical camera id %s has status "
|
||||
+ "changed to 0x%x", id, physicalId, status));
|
||||
}
|
||||
|
||||
if (!validStatus(status)) {
|
||||
Log.e(TAG, String.format(
|
||||
"Ignoring invalid device %s physical device %s status 0x%x", id,
|
||||
physicalId, status));
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: Do we need to treat this as error?
|
||||
if (!mDeviceStatus.containsKey(id) || !isAvailable(mDeviceStatus.get(id))
|
||||
|| !mUnavailablePhysicalDevices.containsKey(id)) {
|
||||
Log.e(TAG, String.format("Camera %s is not available. Ignore physical camera "
|
||||
+ "status change", id));
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<String> unavailablePhysicalDevices = mUnavailablePhysicalDevices.get(id);
|
||||
if (!isAvailable(status)
|
||||
&& !unavailablePhysicalDevices.contains(physicalId)) {
|
||||
unavailablePhysicalDevices.add(physicalId);
|
||||
} else if (isAvailable(status)
|
||||
&& unavailablePhysicalDevices.contains(physicalId)) {
|
||||
unavailablePhysicalDevices.remove(physicalId);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG,
|
||||
String.format(
|
||||
"Physical camera device status was previously available (%b), "
|
||||
+ " and is now again available (%b)"
|
||||
+ "so no new client visible update will be sent",
|
||||
!unavailablePhysicalDevices.contains(physicalId),
|
||||
isAvailable(status)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int callbackCount = mCallbackMap.size();
|
||||
for (int i = 0; i < callbackCount; i++) {
|
||||
Executor executor = mCallbackMap.valueAt(i);
|
||||
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
|
||||
|
||||
postSingleUpdate(callback, executor, id, physicalId, status);
|
||||
}
|
||||
} // onPhysicalCameraStatusChangedLocked
|
||||
|
||||
private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
|
||||
for (int i = 0; i < mTorchStatus.size(); i++) {
|
||||
String id = mTorchStatus.keyAt(i);
|
||||
@@ -1477,6 +1611,14 @@ public final class CameraManager {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhysicalCameraStatusChanged(int status, String cameraId,
|
||||
String physicalCameraId) throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
onPhysicalCameraStatusChangedLocked(status, cameraId, physicalCameraId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
|
||||
@@ -851,14 +851,20 @@ public abstract class CameraMetadata<TKey> {
|
||||
* <p>The camera device is a logical camera backed by two or more physical cameras.</p>
|
||||
* <p>In API level 28, the physical cameras must also be exposed to the application via
|
||||
* {@link android.hardware.camera2.CameraManager#getCameraIdList }.</p>
|
||||
* <p>Starting from API level 29, some or all physical cameras may not be independently
|
||||
* exposed to the application, in which case the physical camera IDs will not be
|
||||
* available in {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the
|
||||
* <p>Starting from API level 29:</p>
|
||||
* <ul>
|
||||
* <li>Some or all physical cameras may not be independently exposed to the application,
|
||||
* in which case the physical camera IDs will not be available in
|
||||
* {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the
|
||||
* application can still query the physical cameras' characteristics by calling
|
||||
* {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }. Additionally,
|
||||
* if a physical camera is hidden from camera ID list, the mandatory stream combinations
|
||||
* for that physical camera must be supported through the logical camera using physical
|
||||
* streams.</p>
|
||||
* {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }.</li>
|
||||
* <li>If a physical camera is hidden from camera ID list, the mandatory stream
|
||||
* combinations for that physical camera must be supported through the logical camera
|
||||
* using physical streams. One exception is that in API level 30, a physical camera
|
||||
* may become unavailable via
|
||||
* {@link CameraManager.AvailabilityCallback#onPhysicalCameraUnavailable }
|
||||
* callback.</li>
|
||||
* </ul>
|
||||
* <p>Combinations of logical and physical streams, or physical streams from different
|
||||
* physical cameras are not guaranteed. However, if the camera device supports
|
||||
* {@link CameraDevice#isSessionConfigurationSupported },
|
||||
|
||||
@@ -311,6 +311,12 @@ public class CameraBinderTest extends AndroidTestCase {
|
||||
cameraId, status));
|
||||
}
|
||||
@Override
|
||||
public void onPhysicalCameraStatusChanged(int status, String cameraId,
|
||||
String physicalCameraId) throws RemoteException {
|
||||
Log.v(TAG, String.format("Camera %s : %s has status changed to 0x%x",
|
||||
cameraId, physicalCameraId, status));
|
||||
}
|
||||
@Override
|
||||
public void onCameraAccessPrioritiesChanged() {
|
||||
Log.v(TAG, "Camera access permission change");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user