Loading services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java +24 −16 Original line number Diff line number Diff line Loading @@ -26,13 +26,14 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.CameraCompatTaskInfo; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.ProtoLog; import com.android.internal.protolog.ProtoLogGroup; import com.android.window.flags.Flags; /** Loading @@ -56,6 +57,9 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa private boolean mIsCameraCompatTreatmentPending = false; @Nullable private Task mCameraTask; CameraCompatFreeformPolicy(@NonNull DisplayContent displayContent, @NonNull CameraStateMonitor cameraStateMonitor, @NonNull ActivityRefresher activityRefresher) { Loading Loading @@ -116,6 +120,7 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa final int newCameraCompatMode = getCameraCompatMode(cameraActivity); if (newCameraCompatMode != existingCameraCompatMode) { mIsCameraCompatTreatmentPending = true; mCameraTask = cameraActivity.getTask(); cameraActivity.mAppCompatController.getAppCompatCameraOverrides() .setFreeformCameraCompatMode(newCameraCompatMode); forceUpdateActivityAndTask(cameraActivity); Loading @@ -127,18 +132,22 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa } @Override public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId) { if (isActivityForCameraIdRefreshing(cameraId)) { public boolean onCameraClosed(@NonNull String cameraId) { // Top activity in the same task as the camera activity, or `null` if the task is // closed. final ActivityRecord topActivity = mCameraTask != null ? mCameraTask.getTopActivity(/* isFinishing */ false, /* includeOverlays */ false) : null; if (topActivity != null) { if (isActivityForCameraIdRefreshing(topActivity, cameraId)) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_STATES, "Display id=%d is notified that Camera %s is closed but activity is" + " still refreshing. Rescheduling an update.", mDisplayContent.mDisplayId, cameraId); return false; } cameraActivity.mAppCompatController.getAppCompatCameraOverrides() .setFreeformCameraCompatMode(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE); forceUpdateActivityAndTask(cameraActivity); } mCameraTask = null; mIsCameraCompatTreatmentPending = false; return true; } Loading Loading @@ -186,10 +195,9 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa && !activity.isEmbedded(); } private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) { final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity == null || !isTreatmentEnabledForActivity(topActivity) private boolean isActivityForCameraIdRefreshing(@NonNull ActivityRecord topActivity, @NonNull String cameraId) { if (!isTreatmentEnabledForActivity(topActivity) || mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) { return false; } Loading services/core/java/com/android/server/wm/CameraStateMonitor.java +44 −29 Original line number Diff line number Diff line Loading @@ -61,9 +61,6 @@ class CameraStateMonitor { @NonNull private final Handler mHandler; @Nullable private ActivityRecord mCameraActivity; // Bi-directional map between package names and active camera IDs since we need to 1) get a // camera id by a package name when resizing the window; 2) get a package name by a camera id // when camera connection is closed and we need to clean up our records. Loading Loading @@ -91,13 +88,13 @@ class CameraStateMonitor { @Override public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) { synchronized (mWmService.mGlobalLock) { notifyCameraOpened(cameraId, packageId); notifyCameraOpenedWithDelay(cameraId, packageId); } } @Override public void onCameraClosed(@NonNull String cameraId) { synchronized (mWmService.mGlobalLock) { notifyCameraClosed(cameraId); notifyCameraClosedWithDelay(cameraId); } } }; Loading Loading @@ -131,8 +128,8 @@ class CameraStateMonitor { mCameraStateListeners.remove(listener); } private void notifyCameraOpened( @NonNull String cameraId, @NonNull String packageName) { private void notifyCameraOpenedWithDelay(@NonNull String cameraId, @NonNull String packageName) { // If an activity is restarting or camera is flipping, the camera connection can be // quickly closed and reopened. mScheduledToBeRemovedCameraIdSet.remove(cameraId); Loading @@ -142,8 +139,11 @@ class CameraStateMonitor { // Some apps can’t handle configuration changes coming at the same time with Camera setup so // delaying orientation update to accommodate for that. mScheduledCompatModeUpdateCameraIdSet.add(cameraId); mHandler.postDelayed( () -> { mHandler.postDelayed(() -> notifyCameraOpenedInternal(cameraId, packageName), CAMERA_OPENED_LETTERBOX_UPDATE_DELAY_MS); } private void notifyCameraOpenedInternal(@NonNull String cameraId, @NonNull String packageName) { synchronized (mWmService.mGlobalLock) { if (!mScheduledCompatModeUpdateCameraIdSet.remove(cameraId)) { // Camera compat mode update has happened already or was cancelled Loading @@ -151,14 +151,16 @@ class CameraStateMonitor { return; } mCameraIdPackageBiMapping.put(packageName, cameraId); mCameraActivity = findCameraActivity(packageName); if (mCameraActivity == null || mCameraActivity.getTask() == null) { // If there are multiple activities of the same package name and none of // them are the top running activity, we do not apply treatment (rather than // guessing and applying it to the wrong activity). final ActivityRecord cameraActivity = findUniqueActivityWithPackageName(packageName); if (cameraActivity == null || cameraActivity.getTask() == null) { return; } notifyListenersCameraOpened(mCameraActivity, cameraId); notifyListenersCameraOpened(cameraActivity, cameraId); } }, CAMERA_OPENED_LETTERBOX_UPDATE_DELAY_MS); } private void notifyListenersCameraOpened(@NonNull ActivityRecord cameraActivity, Loading @@ -174,7 +176,13 @@ class CameraStateMonitor { } } private void notifyCameraClosed(@NonNull String cameraId) { /** * Processes camera closed, and schedules notifying listeners. * * <p>The delay is introduced to avoid flickering when switching between front and back camera, * and when an activity is refreshed due to camera compat treatment. */ private void notifyCameraClosedWithDelay(@NonNull String cameraId) { ProtoLog.v(WM_DEBUG_STATES, "Display id=%d is notified that Camera %s is closed.", mDisplayContent.mDisplayId, cameraId); Loading Loading @@ -217,9 +225,10 @@ class CameraStateMonitor { // Already reconnected to this camera, no need to clean up. return; } if (mCameraActivity != null && mCurrentListenerForCameraActivity != null) { if (mCurrentListenerForCameraActivity != null) { boolean closeSuccessful = mCurrentListenerForCameraActivity.onCameraClosed(mCameraActivity, cameraId); mCurrentListenerForCameraActivity.onCameraClosed(cameraId); if (closeSuccessful) { mCameraIdPackageBiMapping.removeCameraId(cameraId); mCurrentListenerForCameraActivity = null; Loading @@ -231,8 +240,14 @@ class CameraStateMonitor { } // TODO(b/335165310): verify that this works in multi instance and permission dialogs. /** * Finds a visible activity with the given package name. * * <p>If there are multiple visible activities with a given package name, and none of them are * the `topRunningActivity`, returns null. */ @Nullable private ActivityRecord findCameraActivity(@NonNull String packageName) { private ActivityRecord findUniqueActivityWithPackageName(@NonNull String packageName) { final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity != null && topActivity.packageName.equals(packageName)) { Loading Loading @@ -277,11 +292,11 @@ class CameraStateMonitor { // TODO(b/336474959): try to decouple `cameraId` from the listeners. boolean onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId); /** * Notifies the compat listener that an activity has closed the camera. * Notifies the compat listener that camera is closed. * * @return true if cleanup has been successful - the notifier might try again if false. */ // TODO(b/336474959): try to decouple `cameraId` from the listeners. boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId); boolean onCameraClosed(@NonNull String cameraId); } } services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +21 −15 Original line number Diff line number Diff line Loading @@ -342,12 +342,19 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp } @Override public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId) { public boolean onCameraClosed(@NonNull String cameraId) { // Top activity in the same task as the camera activity, or `null` if the task is // closed. final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity == null) { return true; } synchronized (this) { // TODO(b/336474959): Once refresh is implemented in `CameraCompatFreeformPolicy`, // consider checking this in CameraStateMonitor before notifying the listeners (this). if (isActivityForCameraIdRefreshing(cameraId)) { if (isActivityForCameraIdRefreshing(topActivity, cameraId)) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d is notified that camera is closed but activity is" + " still refreshing. Rescheduling an update.", Loading @@ -355,15 +362,15 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp return false; } } ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d is notified that Camera is closed, updating rotation.", mDisplayContent.mDisplayId); final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity == null // Checking whether an activity in fullscreen rather than the task as this // camera compat treatment doesn't cover activity embedding. || topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { // Checking whether an activity in fullscreen rather than the task as this camera compat // treatment doesn't cover activity embedding. // TODO(b/350495350): Consider checking whether this activity is the camera activity, or // whether the top activity has the same task as the one which opened camera. if (topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { return true; } recomputeConfigurationForCameraCompatIfNeeded(topActivity); Loading @@ -372,14 +379,13 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp } // TODO(b/336474959): Do we need cameraId here? private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) { final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (!isTreatmentEnabledForActivity(topActivity) || !mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) { private boolean isActivityForCameraIdRefreshing(@NonNull ActivityRecord activity, @NonNull String cameraId) { if (!isTreatmentEnabledForActivity(activity) || !mCameraStateMonitor.isCameraWithIdRunningForActivity(activity, cameraId)) { return false; } return mActivityRefresher.isActivityRefreshing(topActivity); return mActivityRefresher.isActivityRefreshing(activity); } private void recomputeConfigurationForCameraCompatIfNeeded( Loading services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java +1 −29 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; Loading Loading @@ -175,43 +174,16 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); callOnActivityConfigurationChanging(mActivity); assertInCameraCompatMode(); assertActivityRefreshRequested(/* refreshRequested */ true); } @Test public void testReconnectedToDifferentCamera_activatesCameraCompatModeAndRefresh() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); callOnActivityConfigurationChanging(mActivity); assertInCameraCompatMode(); assertActivityRefreshRequested(/* refreshRequested */ true); } @Test public void testCameraDisconnected_deactivatesCameraCompatMode() { configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE, WINDOWING_MODE_FREEFORM); // Open camera and test for compat treatment mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); assertInCameraCompatMode(); // Close camera and test for revert mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1); assertNotInCameraCompatMode(); } @Test public void testCameraOpenedForDifferentPackage_notInCameraCompatMode() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); Loading services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java +1 −2 Original line number Diff line number Diff line Loading @@ -256,8 +256,7 @@ public final class CameraStateMonitorTests extends WindowTestsBase { } @Override public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId) { public boolean onCameraClosed(@NonNull String cameraId) { mOnCameraClosedCounter++; boolean returnValue = mOnCameraClosedReturnValue; // If false, return false only the first time, so it doesn't fall in the infinite retry Loading Loading
services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java +24 −16 Original line number Diff line number Diff line Loading @@ -26,13 +26,14 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.CameraCompatTaskInfo; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.ProtoLog; import com.android.internal.protolog.ProtoLogGroup; import com.android.window.flags.Flags; /** Loading @@ -56,6 +57,9 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa private boolean mIsCameraCompatTreatmentPending = false; @Nullable private Task mCameraTask; CameraCompatFreeformPolicy(@NonNull DisplayContent displayContent, @NonNull CameraStateMonitor cameraStateMonitor, @NonNull ActivityRefresher activityRefresher) { Loading Loading @@ -116,6 +120,7 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa final int newCameraCompatMode = getCameraCompatMode(cameraActivity); if (newCameraCompatMode != existingCameraCompatMode) { mIsCameraCompatTreatmentPending = true; mCameraTask = cameraActivity.getTask(); cameraActivity.mAppCompatController.getAppCompatCameraOverrides() .setFreeformCameraCompatMode(newCameraCompatMode); forceUpdateActivityAndTask(cameraActivity); Loading @@ -127,18 +132,22 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa } @Override public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId) { if (isActivityForCameraIdRefreshing(cameraId)) { public boolean onCameraClosed(@NonNull String cameraId) { // Top activity in the same task as the camera activity, or `null` if the task is // closed. final ActivityRecord topActivity = mCameraTask != null ? mCameraTask.getTopActivity(/* isFinishing */ false, /* includeOverlays */ false) : null; if (topActivity != null) { if (isActivityForCameraIdRefreshing(topActivity, cameraId)) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_STATES, "Display id=%d is notified that Camera %s is closed but activity is" + " still refreshing. Rescheduling an update.", mDisplayContent.mDisplayId, cameraId); return false; } cameraActivity.mAppCompatController.getAppCompatCameraOverrides() .setFreeformCameraCompatMode(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE); forceUpdateActivityAndTask(cameraActivity); } mCameraTask = null; mIsCameraCompatTreatmentPending = false; return true; } Loading Loading @@ -186,10 +195,9 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa && !activity.isEmbedded(); } private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) { final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity == null || !isTreatmentEnabledForActivity(topActivity) private boolean isActivityForCameraIdRefreshing(@NonNull ActivityRecord topActivity, @NonNull String cameraId) { if (!isTreatmentEnabledForActivity(topActivity) || mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) { return false; } Loading
services/core/java/com/android/server/wm/CameraStateMonitor.java +44 −29 Original line number Diff line number Diff line Loading @@ -61,9 +61,6 @@ class CameraStateMonitor { @NonNull private final Handler mHandler; @Nullable private ActivityRecord mCameraActivity; // Bi-directional map between package names and active camera IDs since we need to 1) get a // camera id by a package name when resizing the window; 2) get a package name by a camera id // when camera connection is closed and we need to clean up our records. Loading Loading @@ -91,13 +88,13 @@ class CameraStateMonitor { @Override public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) { synchronized (mWmService.mGlobalLock) { notifyCameraOpened(cameraId, packageId); notifyCameraOpenedWithDelay(cameraId, packageId); } } @Override public void onCameraClosed(@NonNull String cameraId) { synchronized (mWmService.mGlobalLock) { notifyCameraClosed(cameraId); notifyCameraClosedWithDelay(cameraId); } } }; Loading Loading @@ -131,8 +128,8 @@ class CameraStateMonitor { mCameraStateListeners.remove(listener); } private void notifyCameraOpened( @NonNull String cameraId, @NonNull String packageName) { private void notifyCameraOpenedWithDelay(@NonNull String cameraId, @NonNull String packageName) { // If an activity is restarting or camera is flipping, the camera connection can be // quickly closed and reopened. mScheduledToBeRemovedCameraIdSet.remove(cameraId); Loading @@ -142,8 +139,11 @@ class CameraStateMonitor { // Some apps can’t handle configuration changes coming at the same time with Camera setup so // delaying orientation update to accommodate for that. mScheduledCompatModeUpdateCameraIdSet.add(cameraId); mHandler.postDelayed( () -> { mHandler.postDelayed(() -> notifyCameraOpenedInternal(cameraId, packageName), CAMERA_OPENED_LETTERBOX_UPDATE_DELAY_MS); } private void notifyCameraOpenedInternal(@NonNull String cameraId, @NonNull String packageName) { synchronized (mWmService.mGlobalLock) { if (!mScheduledCompatModeUpdateCameraIdSet.remove(cameraId)) { // Camera compat mode update has happened already or was cancelled Loading @@ -151,14 +151,16 @@ class CameraStateMonitor { return; } mCameraIdPackageBiMapping.put(packageName, cameraId); mCameraActivity = findCameraActivity(packageName); if (mCameraActivity == null || mCameraActivity.getTask() == null) { // If there are multiple activities of the same package name and none of // them are the top running activity, we do not apply treatment (rather than // guessing and applying it to the wrong activity). final ActivityRecord cameraActivity = findUniqueActivityWithPackageName(packageName); if (cameraActivity == null || cameraActivity.getTask() == null) { return; } notifyListenersCameraOpened(mCameraActivity, cameraId); notifyListenersCameraOpened(cameraActivity, cameraId); } }, CAMERA_OPENED_LETTERBOX_UPDATE_DELAY_MS); } private void notifyListenersCameraOpened(@NonNull ActivityRecord cameraActivity, Loading @@ -174,7 +176,13 @@ class CameraStateMonitor { } } private void notifyCameraClosed(@NonNull String cameraId) { /** * Processes camera closed, and schedules notifying listeners. * * <p>The delay is introduced to avoid flickering when switching between front and back camera, * and when an activity is refreshed due to camera compat treatment. */ private void notifyCameraClosedWithDelay(@NonNull String cameraId) { ProtoLog.v(WM_DEBUG_STATES, "Display id=%d is notified that Camera %s is closed.", mDisplayContent.mDisplayId, cameraId); Loading Loading @@ -217,9 +225,10 @@ class CameraStateMonitor { // Already reconnected to this camera, no need to clean up. return; } if (mCameraActivity != null && mCurrentListenerForCameraActivity != null) { if (mCurrentListenerForCameraActivity != null) { boolean closeSuccessful = mCurrentListenerForCameraActivity.onCameraClosed(mCameraActivity, cameraId); mCurrentListenerForCameraActivity.onCameraClosed(cameraId); if (closeSuccessful) { mCameraIdPackageBiMapping.removeCameraId(cameraId); mCurrentListenerForCameraActivity = null; Loading @@ -231,8 +240,14 @@ class CameraStateMonitor { } // TODO(b/335165310): verify that this works in multi instance and permission dialogs. /** * Finds a visible activity with the given package name. * * <p>If there are multiple visible activities with a given package name, and none of them are * the `topRunningActivity`, returns null. */ @Nullable private ActivityRecord findCameraActivity(@NonNull String packageName) { private ActivityRecord findUniqueActivityWithPackageName(@NonNull String packageName) { final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity != null && topActivity.packageName.equals(packageName)) { Loading Loading @@ -277,11 +292,11 @@ class CameraStateMonitor { // TODO(b/336474959): try to decouple `cameraId` from the listeners. boolean onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId); /** * Notifies the compat listener that an activity has closed the camera. * Notifies the compat listener that camera is closed. * * @return true if cleanup has been successful - the notifier might try again if false. */ // TODO(b/336474959): try to decouple `cameraId` from the listeners. boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId); boolean onCameraClosed(@NonNull String cameraId); } }
services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +21 −15 Original line number Diff line number Diff line Loading @@ -342,12 +342,19 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp } @Override public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId) { public boolean onCameraClosed(@NonNull String cameraId) { // Top activity in the same task as the camera activity, or `null` if the task is // closed. final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity == null) { return true; } synchronized (this) { // TODO(b/336474959): Once refresh is implemented in `CameraCompatFreeformPolicy`, // consider checking this in CameraStateMonitor before notifying the listeners (this). if (isActivityForCameraIdRefreshing(cameraId)) { if (isActivityForCameraIdRefreshing(topActivity, cameraId)) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d is notified that camera is closed but activity is" + " still refreshing. Rescheduling an update.", Loading @@ -355,15 +362,15 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp return false; } } ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d is notified that Camera is closed, updating rotation.", mDisplayContent.mDisplayId); final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (topActivity == null // Checking whether an activity in fullscreen rather than the task as this // camera compat treatment doesn't cover activity embedding. || topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { // Checking whether an activity in fullscreen rather than the task as this camera compat // treatment doesn't cover activity embedding. // TODO(b/350495350): Consider checking whether this activity is the camera activity, or // whether the top activity has the same task as the one which opened camera. if (topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { return true; } recomputeConfigurationForCameraCompatIfNeeded(topActivity); Loading @@ -372,14 +379,13 @@ final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraComp } // TODO(b/336474959): Do we need cameraId here? private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) { final ActivityRecord topActivity = mDisplayContent.topRunningActivity( /* considerKeyguardState= */ true); if (!isTreatmentEnabledForActivity(topActivity) || !mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) { private boolean isActivityForCameraIdRefreshing(@NonNull ActivityRecord activity, @NonNull String cameraId) { if (!isTreatmentEnabledForActivity(activity) || !mCameraStateMonitor.isCameraWithIdRunningForActivity(activity, cameraId)) { return false; } return mActivityRefresher.isActivityRefreshing(topActivity); return mActivityRefresher.isActivityRefreshing(activity); } private void recomputeConfigurationForCameraCompatIfNeeded( Loading
services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java +1 −29 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; Loading Loading @@ -175,43 +174,16 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); callOnActivityConfigurationChanging(mActivity); assertInCameraCompatMode(); assertActivityRefreshRequested(/* refreshRequested */ true); } @Test public void testReconnectedToDifferentCamera_activatesCameraCompatModeAndRefresh() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); callOnActivityConfigurationChanging(mActivity); assertInCameraCompatMode(); assertActivityRefreshRequested(/* refreshRequested */ true); } @Test public void testCameraDisconnected_deactivatesCameraCompatMode() { configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE, WINDOWING_MODE_FREEFORM); // Open camera and test for compat treatment mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); assertInCameraCompatMode(); // Close camera and test for revert mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1); assertNotInCameraCompatMode(); } @Test public void testCameraOpenedForDifferentPackage_notInCameraCompatMode() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); Loading
services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java +1 −2 Original line number Diff line number Diff line Loading @@ -256,8 +256,7 @@ public final class CameraStateMonitorTests extends WindowTestsBase { } @Override public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId) { public boolean onCameraClosed(@NonNull String cameraId) { mOnCameraClosedCounter++; boolean returnValue = mOnCameraClosedReturnValue; // If false, return false only the first time, so it doesn't fall in the infinite retry Loading