Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d7929c5 authored by Shuzhen Wang's avatar Shuzhen Wang Committed by Android (Google) Code Review
Browse files

Merge "CameraManager: Add physical camera availability callback"

parents 99025680 34bf53fc
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -17259,6 +17259,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 {
+3 −0
Original line number Diff line number Diff line
@@ -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 =
+147 −5
Original line number Diff line number Diff line
@@ -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() {
                                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() {
                                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) {
+13 −7
Original line number Diff line number Diff line
@@ -865,14 +865,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 },
+6 −0
Original line number Diff line number Diff line
@@ -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");
        }