diff options
| author | 2020-01-15 10:49:52 -0800 | |
|---|---|---|
| committer | 2020-01-22 00:17:31 -0800 | |
| commit | 34bf53fc6cc6668eee07c9319ad2a451fe045f78 (patch) | |
| tree | f8f014ce347cb2fad5056d1d4e66f5c710117ca7 | |
| parent | 871fe0a968e39dc4c78d2ee6e82cb5f15d183d20 (diff) | |
CameraManager: Add physical camera availability callback
Add CameraManager callback for physical camera availability.
Bug: 119325027
Test: Camera CTS
Change-Id: Ibe0357f5034769511576cc71c04365a1009a2be1
5 files changed, 171 insertions, 12 deletions
diff --git a/api/current.txt b/api/current.txt index 1c03c23a0e01..15f8de43c0fa 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 { diff --git a/core/java/android/hardware/CameraStatus.java b/core/java/android/hardware/CameraStatus.java index 08b5b776c94e..29802cb33c38 100644 --- a/core/java/android/hardware/CameraStatus.java +++ b/core/java/android/hardware/CameraStatus.java @@ -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 = diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 9b58578e3811..55025f0411f9 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -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); @@ -1478,6 +1612,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) { onTorchStatusChangedLocked(status, cameraId); diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 2377ccde5f89..5d9adcc6803f 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -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 }, diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index f979fddd7bda..c529952843a1 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -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"); } |