summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java40
-rw-r--r--services/core/java/com/android/server/wm/CameraStateMonitor.java73
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java3
5 files changed, 91 insertions, 91 deletions
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 68a417289c55..2755a80ea705 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -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;
/**
@@ -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) {
@@ -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);
@@ -127,18 +132,22 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa
}
@Override
- public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity,
- @NonNull String cameraId) {
- if (isActivityForCameraIdRefreshing(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;
+ 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;
}
@@ -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;
}
diff --git a/services/core/java/com/android/server/wm/CameraStateMonitor.java b/services/core/java/com/android/server/wm/CameraStateMonitor.java
index a54141ce5230..068fc001ae2c 100644
--- a/services/core/java/com/android/server/wm/CameraStateMonitor.java
+++ b/services/core/java/com/android/server/wm/CameraStateMonitor.java
@@ -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.
@@ -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);
}
}
};
@@ -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);
@@ -142,25 +139,30 @@ 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(
- () -> {
- synchronized (mWmService.mGlobalLock) {
- if (!mScheduledCompatModeUpdateCameraIdSet.remove(cameraId)) {
- // Camera compat mode update has happened already or was cancelled
- // because camera was closed.
- return;
- }
- mCameraIdPackageBiMapping.put(packageName, cameraId);
- mCameraActivity = findCameraActivity(packageName);
- if (mCameraActivity == null || mCameraActivity.getTask() == null) {
- return;
- }
- notifyListenersCameraOpened(mCameraActivity, cameraId);
- }
- },
+ 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
+ // because camera was closed.
+ return;
+ }
+ mCameraIdPackageBiMapping.put(packageName, cameraId);
+ // 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(cameraActivity, cameraId);
+ }
+ }
+
private void notifyListenersCameraOpened(@NonNull ActivityRecord cameraActivity,
@NonNull String cameraId) {
for (int i = 0; i < mCameraStateListeners.size(); i++) {
@@ -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);
@@ -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;
@@ -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)) {
@@ -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);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 9998e1a016d0..1a0124a1d4de 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -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.",
@@ -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);
@@ -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(
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index 564c29f09bc9..11e6d90a5f50 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -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;
@@ -176,22 +175,9 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
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();
@@ -199,20 +185,6 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
}
@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);
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
index e468fd8ee495..1c8dc05c6787 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
@@ -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