Loading services/core/java/com/android/server/wm/ActivityRecord.java +8 −0 Original line number Diff line number Diff line Loading @@ -8793,6 +8793,14 @@ final class ActivityRecord extends WindowToken { if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) { return; } // Notify that the activity is already relaunching, therefore there's no need to refresh // the activity if it was requested. Activity refresher will track activity lifecycle // if needed. if (Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { AppCompatCameraPolicy.onActivityRelaunching(this); } if (!preserveWindow) { // If the activity is the IME input target, ensure storing the last IME shown state // before relaunching it for restoring the IME visibility once its new window focused. Loading services/core/java/com/android/server/wm/ActivityRefresher.java +101 −20 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES; import static com.android.server.wm.AppCompatCameraOverrides.NONE; import static com.android.server.wm.AppCompatCameraOverrides.IN_PROGRESS; import static com.android.server.wm.AppCompatCameraOverrides.REQUESTED; import android.annotation.NonNull; import android.app.servertransaction.RefreshCallbackItem; Loading @@ -28,15 +31,15 @@ import android.os.Handler; import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.window.flags.Flags; import java.util.ArrayList; /** * Class that refreshes the activity (through stop/pause -> resume) based on configuration change. * * <p>This class queries all of its {@link Evaluator}s and restarts the activity if any of them * return {@code true} in {@link Evaluator#shouldRefreshActivity}. {@link ActivityRefresher} cycles * through either stop or pause and then resume, based on the global config and per-app override. * <p>{@link ActivityRefresher} cycles the activity through either stop or pause and then resume, * based on the global config and per-app override. */ class ActivityRefresher { // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The Loading @@ -46,6 +49,7 @@ class ActivityRefresher { @NonNull private final WindowManagerService mWmService; @NonNull private final Handler mHandler; // TODO(b/395063101): remove once external camera sandboxing is launched. @NonNull private final ArrayList<Evaluator> mEvaluators = new ArrayList<>(); ActivityRefresher(@NonNull WindowManagerService wmService, @NonNull Handler handler) { Loading @@ -53,14 +57,40 @@ class ActivityRefresher { mHandler = handler; } // TODO(b/395063101): remove once external display sandboxing for camera is launched. void addEvaluator(@NonNull Evaluator evaluator) { mEvaluators.add(evaluator); } // TODO(b/395063101): remove once external display sandboxing for camera is launched. void removeEvaluator(@NonNull Evaluator evaluator) { mEvaluators.remove(evaluator); } /** * If the activity refresh is requested, mark as pending since activity is already relaunching. * * <p>This is to avoid unnecessary refresh (i.e. cycling activity though stop/pause -> resume), * if refresh is requested for camera compat. * * <p>As camera connection will most likely close and reopen due to relaunch/refresh - which is * the goal, to setup camera with new parameters - this method is setting state to * {@code IN_PROGRESS} instead of {@code NONE} to avoid unnecessary tear down and setup of * camera compat mode. */ void onActivityRelaunching(@NonNull ActivityRecord activity) { if (getActivityRefreshState(activity) == REQUESTED) { // No need to cycle through stop/pause -> resume if the activity is already relaunching // due to config change. activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(IN_PROGRESS); } } void requestRefresh(@NonNull ActivityRecord activity) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(REQUESTED); } // TODO(b/395063101): remove once external display sandboxing for camera is launched. /** * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle. * This allows to clear cached values in apps (e.g. display or camera rotation) that influence Loading @@ -73,13 +103,43 @@ class ActivityRefresher { return; } activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(true); if (refreshActivity(activity)) { scheduleClearIsRefreshing(activity); } else { activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); } } /** * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle. * This allows to clear cached values in apps (e.g. display or camera rotation) that influence * camera preview and can lead to sideways or stretching issues persisting even after force * rotation. */ void refreshActivityIfEnabled(@NonNull ActivityRecord activity) { if (!shouldRefreshActivity(activity)) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(NONE); return; } if (getActivityRefreshState(activity) == REQUESTED) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(IN_PROGRESS); if (!refreshActivity(activity)) { clearRefreshState(activity); return; } } scheduleClearIsRefreshing(activity); } private boolean refreshActivity(@NonNull ActivityRecord activity) { final boolean cycleThroughStop = mWmService.mAppCompatConfiguration .isCameraCompatRefreshCycleThroughStopEnabled() && !activity.mAppCompatController.getCameraOverrides() .shouldRefreshActivityViaPauseForCameraCompat(); activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(true); ProtoLog.v(WM_DEBUG_STATES, "Refreshing activity for freeform camera compatibility treatment, " + "activityRecord=%s", activity); Loading @@ -87,31 +147,46 @@ class ActivityRefresher { new RefreshCallbackItem(activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); final ResumeActivityItem resumeActivityItem = new ResumeActivityItem( activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); final boolean isSuccessful = activity.mAtmService.getLifecycleManager() .scheduleTransactionItems(activity.app.getThread(), refreshCallbackItem, resumeActivityItem); if (isSuccessful) { return activity.mAtmService.getLifecycleManager().scheduleTransactionItems( activity.app.getThread(), refreshCallbackItem, resumeActivityItem); } private void scheduleClearIsRefreshing(@NonNull ActivityRecord activity) { // Clear refresh state after a delay in case something goes wrong. mHandler.postDelayed(() -> { synchronized (mWmService.mGlobalLock) { onActivityRefreshed(activity); } }, REFRESH_CALLBACK_TIMEOUT_MS); } else { activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); } void clearRefreshState(@NonNull ActivityRecord activity) { if (Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(NONE); } activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); } boolean isActivityRefreshing(@NonNull ActivityRecord activity) { return activity.mAppCompatController.getCameraOverrides().isRefreshRequested(); return Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix() ? getActivityRefreshState(activity) == IN_PROGRESS : activity.mAppCompatController.getCameraOverrides().isRefreshRequested(); } void onActivityRefreshed(@NonNull ActivityRecord activity) { // TODO(b/333060789): can we tell that refresh did not happen by observing the activity // state? activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); clearRefreshState(activity); } private boolean shouldRefreshActivity(@NonNull ActivityRecord activity) { return mWmService.mAppCompatConfiguration.isCameraCompatRefreshEnabled() && activity.mAppCompatController.getCameraOverrides() .shouldRefreshActivityForCameraCompat() && getActivityRefreshState(activity) != NONE; } // TODO(b/395063101): remove once external display sandboxing for camera is launched. private boolean shouldRefreshActivity(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { return mWmService.mAppCompatConfiguration.isCameraCompatRefreshEnabled() Loading @@ -122,6 +197,12 @@ class ActivityRefresher { .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null; } private @AppCompatCameraOverrides.ActivityRefreshState int getActivityRefreshState( @NonNull ActivityRecord activity) { return activity.mAppCompatController.getCameraOverrides().getActivityRefreshState(); } // TODO(b/395063101): remove once external display sandboxing for camera is launched. /** * Interface for classes that would like to refresh the recently updated activity, based on the * configuration change. Loading services/core/java/com/android/server/wm/AppCompatCameraOverrides.java +45 −2 Original line number Diff line number Diff line Loading @@ -36,12 +36,15 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.AppCompatUtils.isChangeEnabled; import android.annotation.IntDef; import android.annotation.NonNull; import android.util.proto.ProtoOutputStream; import android.window.DesktopModeFlags; import com.android.server.wm.utils.OptPropFactory; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.function.BooleanSupplier; /** Loading @@ -52,6 +55,24 @@ class AppCompatCameraOverrides { private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatCameraOverrides" : TAG_ATM; /** There is no active or requested refresh for the activity. */ static final int NONE = 0; /** A request was made for this activity to be refreshed, but it hasn't started yet. */ static final int REQUESTED = 1; /** The activity is currently refreshing. */ static final int IN_PROGRESS = 2; @IntDef(value = { NONE, REQUESTED, IN_PROGRESS, }) @Retention(RetentionPolicy.SOURCE) public @interface ActivityRefreshState {} @NonNull private final ActivityRecord mActivityRecord; @NonNull Loading Loading @@ -213,6 +234,22 @@ class AppCompatCameraOverrides { return isChangeEnabled(mActivityRecord, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA); } /** * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}. */ @ActivityRefreshState int getActivityRefreshState() { return mAppCompatCameraOverridesState.mActivityRefreshState; } /** * @param refreshState Whether activity "refresh" is requested, pending (in progress), or not * needed, in {@link #activityResumedLocked}. */ void setActivityRefreshState(@ActivityRefreshState int refreshState) { mAppCompatCameraOverridesState.mActivityRefreshState = refreshState; } /** * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}. */ Loading Loading @@ -248,9 +285,15 @@ class AppCompatCameraOverrides { } static class AppCompatCameraOverridesState { // Whether activity "refresh" was requested, in progress, or not needed, following the // camera compat treatment applied by AppCompatCameraSimReqOrientationPolicy // (AppCompatCameraDisplayRotationPolicy is being sunset so it is left unchanged). @ActivityRefreshState private int mActivityRefreshState = NONE; // Whether activity "refresh" was requested but not finished in // ActivityRecord#activityResumedLocked following the camera compat force rotation in // DisplayRotationCompatPolicy. // ActivityRecord#activityResumedLocked following the camera compat treatment applied by // AppCompatCameraDisplayRotationPolicy or AppCompatCameraSimReqOrientationPolicy. private boolean mIsRefreshRequested; } } services/core/java/com/android/server/wm/AppCompatCameraPolicy.java +13 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,19 @@ class AppCompatCameraPolicy { } } /** * Notifies {@link ActivityRefresher} that the activity is already relaunching. * * <p>This is to avoid unnecessary refresh (i.e. cycling activity though stop/pause -> resume), * if refresh is pending for camera compat. */ static void onActivityRelaunching(@NonNull ActivityRecord activity) { final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity); if (cameraPolicy != null && cameraPolicy.mActivityRefresher != null) { cameraPolicy.mActivityRefresher.onActivityRelaunching(activity); } } /** * Notifies that animation in {@link ScreenRotationAnimation} has finished. * Loading services/core/java/com/android/server/wm/AppCompatCameraSimReqOrientationPolicy.java +28 −12 Original line number Diff line number Diff line Loading @@ -95,7 +95,9 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta void start() { mCameraStateNotifier.addCameraStatePolicy(this); if (!Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { mActivityRefresher.addEvaluator(this); } mCameraDisplayRotationProvider.start(); mIsRunning = true; } Loading @@ -103,7 +105,9 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta /** Releases camera callback listener. */ void dispose() { mCameraStateNotifier.removeCameraStatePolicy(this); if (!Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { mActivityRefresher.removeEvaluator(this); } mCameraDisplayRotationProvider.dispose(); mIsRunning = false; } Loading Loading @@ -204,32 +208,37 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta private void updateAndDispatchCameraConfiguration(@Nullable WindowProcessController app, @Nullable Task task) { final ActivityRecord activity = getTopActivityFromCameraTask(task); final boolean isCompatActivity = activity != null && isCompatibilityTreatmentEnabledForActivity(activity, /*checkOrientation=*/ false); // Only apps that need letterboxing (compatibility apps) need to recalculate configuration. if (isCompatActivity) { if (activity != null) { activity.recomputeConfiguration(); } if (task != null && isCompatActivity) { if (task != null) { task.dispatchTaskInfoChangedIfNeeded(/* force= */ true); } if (app != null) { updateCompatibilityInfo(app, activity); final boolean refreshNeeded = updateCompatibilityInfo(app, activity); if (activity != null && Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix() && refreshNeeded) { mActivityRefresher.requestRefresh(activity); } } if (activity != null) { // Refresh the activity, to get the app to reconfigure the camera setup. activity.ensureActivityConfiguration(/* ignoreVisibility= */ true); if (Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { mActivityRefresher.refreshActivityIfEnabled(activity); } } } private void updateCompatibilityInfo(@NonNull WindowProcessController app, private boolean updateCompatibilityInfo(@NonNull WindowProcessController app, @Nullable ActivityRecord activityRecord) { if (app.getThread() == null || app.mInfo == null) { Slog.w(TAG, "Insufficient app information. Cannot revert display rotation sandboxing."); return; return false; } boolean needsRefresh = false; // CompatibilityInfo fields are static, so even if task or activity has been closed, this // state should be updated in case the app process is still alive. final CompatibilityInfo compatibilityInfo = mAtmService Loading @@ -253,6 +262,7 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta displayRotation)) // TODO(b/365725400): support landscape cameras. .setShouldOverrideSensorOrientation(false); needsRefresh = true; } else if (mCameraStateMonitor.isCameraRunningForActivity(activityRecord)) { // Sandbox only display rotation if needed, for external display. // TODO(b/395063101): signal the camera to not apply Loading @@ -262,6 +272,7 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta // framework has no information to avoid doing this. cameraCompatibilityInfoBuilder.setDisplayRotationSandbox( mCameraDisplayRotationProvider.getCameraDeviceRotation()); needsRefresh = true; } } Loading @@ -273,7 +284,10 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta } catch (RemoteException e) { ProtoLog.w(WmProtoLogGroups.WM_DEBUG_STATES, "Unable to update CompatibilityInfo for app %s", app); return false; } return needsRefresh; } /** Loading Loading @@ -514,6 +528,8 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta || !mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) { return false; } return topActivity.mAppCompatController.getCameraOverrides().isRefreshRequested(); return Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix() ? mActivityRefresher.isActivityRefreshing(topActivity) : topActivity.mAppCompatController.getCameraOverrides().isRefreshRequested(); } } Loading
services/core/java/com/android/server/wm/ActivityRecord.java +8 −0 Original line number Diff line number Diff line Loading @@ -8793,6 +8793,14 @@ final class ActivityRecord extends WindowToken { if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) { return; } // Notify that the activity is already relaunching, therefore there's no need to refresh // the activity if it was requested. Activity refresher will track activity lifecycle // if needed. if (Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { AppCompatCameraPolicy.onActivityRelaunching(this); } if (!preserveWindow) { // If the activity is the IME input target, ensure storing the last IME shown state // before relaunching it for restoring the IME visibility once its new window focused. Loading
services/core/java/com/android/server/wm/ActivityRefresher.java +101 −20 Original line number Diff line number Diff line Loading @@ -19,6 +19,9 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES; import static com.android.server.wm.AppCompatCameraOverrides.NONE; import static com.android.server.wm.AppCompatCameraOverrides.IN_PROGRESS; import static com.android.server.wm.AppCompatCameraOverrides.REQUESTED; import android.annotation.NonNull; import android.app.servertransaction.RefreshCallbackItem; Loading @@ -28,15 +31,15 @@ import android.os.Handler; import com.android.internal.protolog.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.window.flags.Flags; import java.util.ArrayList; /** * Class that refreshes the activity (through stop/pause -> resume) based on configuration change. * * <p>This class queries all of its {@link Evaluator}s and restarts the activity if any of them * return {@code true} in {@link Evaluator#shouldRefreshActivity}. {@link ActivityRefresher} cycles * through either stop or pause and then resume, based on the global config and per-app override. * <p>{@link ActivityRefresher} cycles the activity through either stop or pause and then resume, * based on the global config and per-app override. */ class ActivityRefresher { // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The Loading @@ -46,6 +49,7 @@ class ActivityRefresher { @NonNull private final WindowManagerService mWmService; @NonNull private final Handler mHandler; // TODO(b/395063101): remove once external camera sandboxing is launched. @NonNull private final ArrayList<Evaluator> mEvaluators = new ArrayList<>(); ActivityRefresher(@NonNull WindowManagerService wmService, @NonNull Handler handler) { Loading @@ -53,14 +57,40 @@ class ActivityRefresher { mHandler = handler; } // TODO(b/395063101): remove once external display sandboxing for camera is launched. void addEvaluator(@NonNull Evaluator evaluator) { mEvaluators.add(evaluator); } // TODO(b/395063101): remove once external display sandboxing for camera is launched. void removeEvaluator(@NonNull Evaluator evaluator) { mEvaluators.remove(evaluator); } /** * If the activity refresh is requested, mark as pending since activity is already relaunching. * * <p>This is to avoid unnecessary refresh (i.e. cycling activity though stop/pause -> resume), * if refresh is requested for camera compat. * * <p>As camera connection will most likely close and reopen due to relaunch/refresh - which is * the goal, to setup camera with new parameters - this method is setting state to * {@code IN_PROGRESS} instead of {@code NONE} to avoid unnecessary tear down and setup of * camera compat mode. */ void onActivityRelaunching(@NonNull ActivityRecord activity) { if (getActivityRefreshState(activity) == REQUESTED) { // No need to cycle through stop/pause -> resume if the activity is already relaunching // due to config change. activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(IN_PROGRESS); } } void requestRefresh(@NonNull ActivityRecord activity) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(REQUESTED); } // TODO(b/395063101): remove once external display sandboxing for camera is launched. /** * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle. * This allows to clear cached values in apps (e.g. display or camera rotation) that influence Loading @@ -73,13 +103,43 @@ class ActivityRefresher { return; } activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(true); if (refreshActivity(activity)) { scheduleClearIsRefreshing(activity); } else { activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); } } /** * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle. * This allows to clear cached values in apps (e.g. display or camera rotation) that influence * camera preview and can lead to sideways or stretching issues persisting even after force * rotation. */ void refreshActivityIfEnabled(@NonNull ActivityRecord activity) { if (!shouldRefreshActivity(activity)) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(NONE); return; } if (getActivityRefreshState(activity) == REQUESTED) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(IN_PROGRESS); if (!refreshActivity(activity)) { clearRefreshState(activity); return; } } scheduleClearIsRefreshing(activity); } private boolean refreshActivity(@NonNull ActivityRecord activity) { final boolean cycleThroughStop = mWmService.mAppCompatConfiguration .isCameraCompatRefreshCycleThroughStopEnabled() && !activity.mAppCompatController.getCameraOverrides() .shouldRefreshActivityViaPauseForCameraCompat(); activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(true); ProtoLog.v(WM_DEBUG_STATES, "Refreshing activity for freeform camera compatibility treatment, " + "activityRecord=%s", activity); Loading @@ -87,31 +147,46 @@ class ActivityRefresher { new RefreshCallbackItem(activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); final ResumeActivityItem resumeActivityItem = new ResumeActivityItem( activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); final boolean isSuccessful = activity.mAtmService.getLifecycleManager() .scheduleTransactionItems(activity.app.getThread(), refreshCallbackItem, resumeActivityItem); if (isSuccessful) { return activity.mAtmService.getLifecycleManager().scheduleTransactionItems( activity.app.getThread(), refreshCallbackItem, resumeActivityItem); } private void scheduleClearIsRefreshing(@NonNull ActivityRecord activity) { // Clear refresh state after a delay in case something goes wrong. mHandler.postDelayed(() -> { synchronized (mWmService.mGlobalLock) { onActivityRefreshed(activity); } }, REFRESH_CALLBACK_TIMEOUT_MS); } else { activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); } void clearRefreshState(@NonNull ActivityRecord activity) { if (Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { activity.mAppCompatController.getCameraOverrides().setActivityRefreshState(NONE); } activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); } boolean isActivityRefreshing(@NonNull ActivityRecord activity) { return activity.mAppCompatController.getCameraOverrides().isRefreshRequested(); return Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix() ? getActivityRefreshState(activity) == IN_PROGRESS : activity.mAppCompatController.getCameraOverrides().isRefreshRequested(); } void onActivityRefreshed(@NonNull ActivityRecord activity) { // TODO(b/333060789): can we tell that refresh did not happen by observing the activity // state? activity.mAppCompatController.getCameraOverrides().setIsRefreshRequested(false); clearRefreshState(activity); } private boolean shouldRefreshActivity(@NonNull ActivityRecord activity) { return mWmService.mAppCompatConfiguration.isCameraCompatRefreshEnabled() && activity.mAppCompatController.getCameraOverrides() .shouldRefreshActivityForCameraCompat() && getActivityRefreshState(activity) != NONE; } // TODO(b/395063101): remove once external display sandboxing for camera is launched. private boolean shouldRefreshActivity(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { return mWmService.mAppCompatConfiguration.isCameraCompatRefreshEnabled() Loading @@ -122,6 +197,12 @@ class ActivityRefresher { .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null; } private @AppCompatCameraOverrides.ActivityRefreshState int getActivityRefreshState( @NonNull ActivityRecord activity) { return activity.mAppCompatController.getCameraOverrides().getActivityRefreshState(); } // TODO(b/395063101): remove once external display sandboxing for camera is launched. /** * Interface for classes that would like to refresh the recently updated activity, based on the * configuration change. Loading
services/core/java/com/android/server/wm/AppCompatCameraOverrides.java +45 −2 Original line number Diff line number Diff line Loading @@ -36,12 +36,15 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.AppCompatUtils.isChangeEnabled; import android.annotation.IntDef; import android.annotation.NonNull; import android.util.proto.ProtoOutputStream; import android.window.DesktopModeFlags; import com.android.server.wm.utils.OptPropFactory; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.function.BooleanSupplier; /** Loading @@ -52,6 +55,24 @@ class AppCompatCameraOverrides { private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatCameraOverrides" : TAG_ATM; /** There is no active or requested refresh for the activity. */ static final int NONE = 0; /** A request was made for this activity to be refreshed, but it hasn't started yet. */ static final int REQUESTED = 1; /** The activity is currently refreshing. */ static final int IN_PROGRESS = 2; @IntDef(value = { NONE, REQUESTED, IN_PROGRESS, }) @Retention(RetentionPolicy.SOURCE) public @interface ActivityRefreshState {} @NonNull private final ActivityRecord mActivityRecord; @NonNull Loading Loading @@ -213,6 +234,22 @@ class AppCompatCameraOverrides { return isChangeEnabled(mActivityRecord, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA); } /** * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}. */ @ActivityRefreshState int getActivityRefreshState() { return mAppCompatCameraOverridesState.mActivityRefreshState; } /** * @param refreshState Whether activity "refresh" is requested, pending (in progress), or not * needed, in {@link #activityResumedLocked}. */ void setActivityRefreshState(@ActivityRefreshState int refreshState) { mAppCompatCameraOverridesState.mActivityRefreshState = refreshState; } /** * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}. */ Loading Loading @@ -248,9 +285,15 @@ class AppCompatCameraOverrides { } static class AppCompatCameraOverridesState { // Whether activity "refresh" was requested, in progress, or not needed, following the // camera compat treatment applied by AppCompatCameraSimReqOrientationPolicy // (AppCompatCameraDisplayRotationPolicy is being sunset so it is left unchanged). @ActivityRefreshState private int mActivityRefreshState = NONE; // Whether activity "refresh" was requested but not finished in // ActivityRecord#activityResumedLocked following the camera compat force rotation in // DisplayRotationCompatPolicy. // ActivityRecord#activityResumedLocked following the camera compat treatment applied by // AppCompatCameraDisplayRotationPolicy or AppCompatCameraSimReqOrientationPolicy. private boolean mIsRefreshRequested; } }
services/core/java/com/android/server/wm/AppCompatCameraPolicy.java +13 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,19 @@ class AppCompatCameraPolicy { } } /** * Notifies {@link ActivityRefresher} that the activity is already relaunching. * * <p>This is to avoid unnecessary refresh (i.e. cycling activity though stop/pause -> resume), * if refresh is pending for camera compat. */ static void onActivityRelaunching(@NonNull ActivityRecord activity) { final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity); if (cameraPolicy != null && cameraPolicy.mActivityRefresher != null) { cameraPolicy.mActivityRefresher.onActivityRelaunching(activity); } } /** * Notifies that animation in {@link ScreenRotationAnimation} has finished. * Loading
services/core/java/com/android/server/wm/AppCompatCameraSimReqOrientationPolicy.java +28 −12 Original line number Diff line number Diff line Loading @@ -95,7 +95,9 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta void start() { mCameraStateNotifier.addCameraStatePolicy(this); if (!Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { mActivityRefresher.addEvaluator(this); } mCameraDisplayRotationProvider.start(); mIsRunning = true; } Loading @@ -103,7 +105,9 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta /** Releases camera callback listener. */ void dispose() { mCameraStateNotifier.removeCameraStatePolicy(this); if (!Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { mActivityRefresher.removeEvaluator(this); } mCameraDisplayRotationProvider.dispose(); mIsRunning = false; } Loading Loading @@ -204,32 +208,37 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta private void updateAndDispatchCameraConfiguration(@Nullable WindowProcessController app, @Nullable Task task) { final ActivityRecord activity = getTopActivityFromCameraTask(task); final boolean isCompatActivity = activity != null && isCompatibilityTreatmentEnabledForActivity(activity, /*checkOrientation=*/ false); // Only apps that need letterboxing (compatibility apps) need to recalculate configuration. if (isCompatActivity) { if (activity != null) { activity.recomputeConfiguration(); } if (task != null && isCompatActivity) { if (task != null) { task.dispatchTaskInfoChangedIfNeeded(/* force= */ true); } if (app != null) { updateCompatibilityInfo(app, activity); final boolean refreshNeeded = updateCompatibilityInfo(app, activity); if (activity != null && Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix() && refreshNeeded) { mActivityRefresher.requestRefresh(activity); } } if (activity != null) { // Refresh the activity, to get the app to reconfigure the camera setup. activity.ensureActivityConfiguration(/* ignoreVisibility= */ true); if (Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix()) { mActivityRefresher.refreshActivityIfEnabled(activity); } } } private void updateCompatibilityInfo(@NonNull WindowProcessController app, private boolean updateCompatibilityInfo(@NonNull WindowProcessController app, @Nullable ActivityRecord activityRecord) { if (app.getThread() == null || app.mInfo == null) { Slog.w(TAG, "Insufficient app information. Cannot revert display rotation sandboxing."); return; return false; } boolean needsRefresh = false; // CompatibilityInfo fields are static, so even if task or activity has been closed, this // state should be updated in case the app process is still alive. final CompatibilityInfo compatibilityInfo = mAtmService Loading @@ -253,6 +262,7 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta displayRotation)) // TODO(b/365725400): support landscape cameras. .setShouldOverrideSensorOrientation(false); needsRefresh = true; } else if (mCameraStateMonitor.isCameraRunningForActivity(activityRecord)) { // Sandbox only display rotation if needed, for external display. // TODO(b/395063101): signal the camera to not apply Loading @@ -262,6 +272,7 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta // framework has no information to avoid doing this. cameraCompatibilityInfoBuilder.setDisplayRotationSandbox( mCameraDisplayRotationProvider.getCameraDeviceRotation()); needsRefresh = true; } } Loading @@ -273,7 +284,10 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta } catch (RemoteException e) { ProtoLog.w(WmProtoLogGroups.WM_DEBUG_STATES, "Unable to update CompatibilityInfo for app %s", app); return false; } return needsRefresh; } /** Loading Loading @@ -514,6 +528,8 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta || !mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) { return false; } return topActivity.mAppCompatController.getCameraOverrides().isRefreshRequested(); return Flags.enableCameraCompatSandboxDisplayRotationOnExternalDisplaysBugfix() ? mActivityRefresher.isActivityRefreshing(topActivity) : topActivity.mAppCompatController.getCameraOverrides().isRefreshRequested(); } }