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

Commit 68ec9d57 authored by Mina Granic's avatar Mina Granic Committed by Android (Google) Code Review
Browse files

Merge "Notify all camera listeners on open/close." into main

parents 47c54dc7 1ac9b5e9
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -109,10 +109,10 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa
    }

    @Override
    public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
    public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
            @NonNull String cameraId) {
        if (!isTreatmentEnabledForActivity(cameraActivity)) {
            return false;
            return;
        }
        final int existingCameraCompatMode = cameraActivity.mAppCompatController
                .getAppCompatCameraOverrides()
@@ -124,11 +124,9 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa
            cameraActivity.mAppCompatController.getAppCompatCameraOverrides()
                    .setFreeformCameraCompatMode(newCameraCompatMode);
            forceUpdateActivityAndTask(cameraActivity);
            return true;
        } else {
            mIsCameraCompatTreatmentPending = false;
        }
        return false;
    }

    @Override
+22 −28
Original line number Diff line number Diff line
@@ -73,16 +73,6 @@ class CameraStateMonitor {

    private final ArrayList<CameraCompatStateListener> mCameraStateListeners = new ArrayList<>();

    /**
     * {@link CameraCompatStateListener} which returned {@code true} on the last {@link
     * CameraCompatStateListener#onCameraOpened(ActivityRecord, String)}, if any.
     *
     * <p>This allows the {@link CameraStateMonitor} to notify a particular listener when camera
     * closes, so they can revert any changes.
     */
    @Nullable
    private CameraCompatStateListener mCurrentListenerForCameraActivity;

    private final CameraManager.AvailabilityCallback mAvailabilityCallback =
            new  CameraManager.AvailabilityCallback() {
                @Override
@@ -167,12 +157,7 @@ class CameraStateMonitor {
            @NonNull String cameraId) {
        for (int i = 0; i < mCameraStateListeners.size(); i++) {
            CameraCompatStateListener listener = mCameraStateListeners.get(i);
            boolean activeCameraTreatment = listener.onCameraOpened(
                    cameraActivity, cameraId);
            if (activeCameraTreatment) {
                mCurrentListenerForCameraActivity = listener;
                break;
            }
            listener.onCameraOpened(cameraActivity, cameraId);
        }
    }

@@ -226,17 +211,28 @@ class CameraStateMonitor {
                return;
            }

            if (mCurrentListenerForCameraActivity != null) {
                boolean closeSuccessful =
                        mCurrentListenerForCameraActivity.onCameraClosed(cameraId);
                if (closeSuccessful) {
            final boolean closeSuccessfulForAllListeners = notifyListenersCameraClosed(cameraId);
            if (closeSuccessfulForAllListeners) {
                // Finish cleaning up.
                mCameraIdPackageBiMapping.removeCameraId(cameraId);
                    mCurrentListenerForCameraActivity = null;
            } else {
                // Not ready to process closure yet - the camera activity might be refreshing.
                // Try again later.
                rescheduleRemoveCameraActivity(cameraId);
            }
        }
    }

    /**
     * @return {@code false} if any listeners have reported issues processing the close.
     */
    private boolean notifyListenersCameraClosed(@NonNull String cameraId) {
        boolean closeSuccessfulForAllListeners = true;
        for (int i = 0; i < mCameraStateListeners.size(); i++) {
            closeSuccessfulForAllListeners &= mCameraStateListeners.get(i).onCameraClosed(cameraId);
        }

        return closeSuccessfulForAllListeners;
    }

    // TODO(b/335165310): verify that this works in multi instance and permission dialogs.
@@ -286,11 +282,9 @@ class CameraStateMonitor {
    interface CameraCompatStateListener {
        /**
         * Notifies the compat listener that an activity has opened camera.
         *
         * @return true if the treatment has been applied.
         */
        // TODO(b/336474959): try to decouple `cameraId` from the listeners.
        boolean onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId);
        void onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId);
        /**
         * Notifies the compat listener that camera is closed.
         *
+2 −4
Original line number Diff line number Diff line
@@ -298,7 +298,7 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp
    }

    @Override
    public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
    public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
            @NonNull String cameraId) {
        mCameraTask = cameraActivity.getTask();
        // Checking whether an activity in fullscreen rather than the task as this camera
@@ -306,7 +306,7 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp
        if (cameraActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
            recomputeConfigurationForCameraCompatIfNeeded(cameraActivity);
            mDisplayContent.updateOrientation();
            return true;
            return;
        }
        // Checking that the whole app is in multi-window mode as we shouldn't show toast
        // for the activity embedding case.
@@ -320,7 +320,6 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp
                        (String) packageManager.getApplicationLabel(
                                packageManager.getApplicationInfo(cameraActivity.packageName,
                                        /* flags */ 0)));
                return true;
            } catch (PackageManager.NameNotFoundException e) {
                ProtoLog.e(WM_DEBUG_ORIENTATION,
                        "DisplayRotationCompatPolicy: Multi-window toast not shown as "
@@ -328,7 +327,6 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp
                        cameraActivity.packageName);
            }
        }
        return false;
    }

    @VisibleForTesting
+18 −54
Original line number Diff line number Diff line
@@ -43,10 +43,10 @@ import org.junit.runner.RunWith;
import java.util.concurrent.Executor;

/**
 * Tests for {@link DisplayRotationCompatPolicy}.
 * Tests for {@link CameraStateMonitor}.
 *
 * Build/Install/Run:
 *  atest WmTests:DisplayRotationCompatPolicyTests
 *  atest WmTests:CameraStateMonitorTests
 */
@SmallTest
@Presubmit
@@ -68,23 +68,14 @@ public final class CameraStateMonitorTests extends WindowTestsBase {
    private ActivityRecord mActivity;
    private Task mTask;

    // Simulates a listener which will not react to the change on a particular activity.
    private final FakeCameraCompatStateListener mNotInterestedListener =
            new FakeCameraCompatStateListener(
                    /*onCameraOpenedReturnValue=*/ false,
                    /*simulateUnsuccessfulCloseOnce=*/ false);
    // Simulates a listener which will react to the change on a particular activity - for example
    // put the activity in a camera compat mode.
    private final FakeCameraCompatStateListener mInterestedListener =
            new FakeCameraCompatStateListener(
                    /*onCameraOpenedReturnValue=*/ true,
                    /*simulateUnsuccessfulCloseOnce=*/ false);
    private final FakeCameraCompatStateListener mListener =
            new FakeCameraCompatStateListener(/* simulateUnsuccessfulCloseOnce= */ false);
    // Simulates a listener which for some reason cannot process `onCameraClosed` event once it
    // first arrives - this means that the update needs to be postponed.
    private final FakeCameraCompatStateListener mListenerCannotClose =
            new FakeCameraCompatStateListener(
                    /*onCameraOpenedReturnValue=*/ true,
                    /*simulateUnsuccessfulCloseOnce=*/ true);
            new FakeCameraCompatStateListener(/* simulateUnsuccessfulCloseOnce= */ true);

    @Before
    public void setUp() throws Exception {
@@ -129,44 +120,31 @@ public final class CameraStateMonitorTests extends WindowTestsBase {
    @After
    public void tearDown() {
        // Remove all listeners.
        mCameraStateMonitor.removeCameraStateListener(mNotInterestedListener);
        mCameraStateMonitor.removeCameraStateListener(mInterestedListener);
        mCameraStateMonitor.removeCameraStateListener(mListener);
        mCameraStateMonitor.removeCameraStateListener(mListenerCannotClose);

        // Reset the listener's state.
        mNotInterestedListener.resetCounters();
        mInterestedListener.resetCounters();
        mListener.resetCounters();
        mListenerCannotClose.resetCounters();
    }

    @Test
    public void testOnCameraOpened_listenerAdded_notifiesCameraOpened() {
        mCameraStateMonitor.addCameraStateListener(mNotInterestedListener);
        mCameraStateMonitor.addCameraStateListener(mListener);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

        assertEquals(1, mNotInterestedListener.mOnCameraOpenedCounter);
        assertEquals(1, mListener.mOnCameraOpenedCounter);
    }

    @Test
    public void testOnCameraOpened_listenerReturnsFalse_doesNotNotifyCameraClosed() {
        mCameraStateMonitor.addCameraStateListener(mNotInterestedListener);
        // Listener returns false on `onCameraOpened`.
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);

        assertEquals(0, mNotInterestedListener.mOnCameraClosedCounter);
    }

    @Test
    public void testOnCameraOpened_listenerReturnsTrue_notifyCameraClosed() {
        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
    public void testOnCameraOpened_cameraClosed_notifyCameraClosed() {
        mCameraStateMonitor.addCameraStateListener(mListener);
        // Listener returns true on `onCameraOpened`.
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);

        assertEquals(1, mInterestedListener.mOnCameraClosedCounter);
        assertEquals(1, mListener.mOnCameraClosedCounter);
    }

    @Test
@@ -182,32 +160,22 @@ public final class CameraStateMonitorTests extends WindowTestsBase {

    @Test
    public void testReconnectedToDifferentCamera_notifiesListener() {
        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
        mCameraStateMonitor.addCameraStateListener(mListener);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1);

        assertEquals(2, mInterestedListener.mOnCameraOpenedCounter);
        assertEquals(2, mListener.mOnCameraOpenedCounter);
    }

    @Test
    public void testDifferentAppConnectedToCamera_notifiesListener() {
        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
        mCameraStateMonitor.addCameraStateListener(mListener);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);

        assertEquals(2, mInterestedListener.mOnCameraOpenedCounter);
    }

    @Test
    public void testCameraAlreadyClosed_notifiesListenerOnce() {
        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);

        assertEquals(1, mInterestedListener.mOnCameraClosedCounter);
        assertEquals(2, mListener.mOnCameraOpenedCounter);
    }

    private void configureActivity(@NonNull String packageName) {
@@ -232,7 +200,6 @@ public final class CameraStateMonitorTests extends WindowTestsBase {
        int mOnCameraOpenedCounter = 0;
        int mOnCameraClosedCounter = 0;

        boolean mOnCameraOpenedReturnValue = true;
        private boolean mOnCameraClosedReturnValue = true;

        /**
@@ -242,17 +209,14 @@ public final class CameraStateMonitorTests extends WindowTestsBase {
         *                                      subsequent calls. This fake implementation tests the
         *                                      retry mechanism in {@link CameraStateMonitor}.
         */
        FakeCameraCompatStateListener(boolean onCameraOpenedReturnValue,
                boolean simulateUnsuccessfulCloseOnce) {
            mOnCameraOpenedReturnValue = onCameraOpenedReturnValue;
        FakeCameraCompatStateListener(boolean simulateUnsuccessfulCloseOnce) {
            mOnCameraClosedReturnValue = !simulateUnsuccessfulCloseOnce;
        }

        @Override
        public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
        public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
                @NonNull String cameraId) {
            mOnCameraOpenedCounter++;
            return mOnCameraOpenedReturnValue;
        }

        @Override