Loading data/etc/core.protolog.pb −38 B (52.8 KiB) File changed.No diff preview for this file type. View original file View changed file data/etc/services.core.protolog.json +6 −6 Original line number Diff line number Diff line Loading @@ -583,6 +583,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, "7211222997110112110": { "message": "Refreshing activity for freeform camera compatibility treatment, activityRecord=%s", "level": "VERBOSE", "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRefresher.java" }, "1665699123574159131": { "message": "Starting activity when config will change = %b", "level": "VERBOSE", Loading Loading @@ -1771,12 +1777,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" }, "-7756685416834187936": { "message": "Refreshing activity for camera compatibility treatment, activityRecord=%s", "level": "VERBOSE", "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" }, "-5176775281239247368": { "message": "Reverting orientation after camera compat force rotation", "level": "VERBOSE", Loading services/core/java/com/android/server/wm/ActivityRecord.java +7 −7 Original line number Diff line number Diff line Loading @@ -6530,8 +6530,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // and the token could be null. return; } if (r.mDisplayContent.mDisplayRotationCompatPolicy != null) { r.mDisplayContent.mDisplayRotationCompatPolicy.onActivityRefreshed(r); if (r.mDisplayContent.mActivityRefresher != null) { r.mDisplayContent.mActivityRefresher.onActivityRefreshed(r); } } Loading Loading @@ -9996,7 +9996,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo); } notifyDisplayCompatPolicyAboutConfigurationChange( notifyActivityRefresherAboutConfigurationChange( mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig); return true; } Loading Loading @@ -10063,18 +10063,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo); } notifyDisplayCompatPolicyAboutConfigurationChange( notifyActivityRefresherAboutConfigurationChange( mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig); return true; } private void notifyDisplayCompatPolicyAboutConfigurationChange( private void notifyActivityRefresherAboutConfigurationChange( Configuration newConfig, Configuration lastReportedConfig) { if (mDisplayContent.mDisplayRotationCompatPolicy == null if (mDisplayContent.mActivityRefresher == null || !shouldBeResumed(/* activeActivity */ null)) { return; } mDisplayContent.mDisplayRotationCompatPolicy.onActivityConfigurationChanging( mDisplayContent.mActivityRefresher.onActivityConfigurationChanging( this, newConfig, lastReportedConfig); } Loading services/core/java/com/android/server/wm/ActivityRefresher.java 0 → 100644 +131 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm; import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import android.annotation.NonNull; import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; import android.content.res.Configuration; import android.os.Handler; import android.os.RemoteException; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; 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. */ class ActivityRefresher { // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The // client process may not always report the event back to the server, such as process is // crashed or got killed. private static final long REFRESH_CALLBACK_TIMEOUT_MS = 2000L; @NonNull private final WindowManagerService mWmService; @NonNull private final Handler mHandler; @NonNull private final ArrayList<Evaluator> mEvaluators = new ArrayList<>(); ActivityRefresher(@NonNull WindowManagerService wmService, @NonNull Handler handler) { mWmService = wmService; mHandler = handler; } void addEvaluator(@NonNull Evaluator evaluator) { mEvaluators.add(evaluator); } void removeEvaluator(@NonNull Evaluator evaluator) { mEvaluators.remove(evaluator); } /** * "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 onActivityConfigurationChanging(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { if (!shouldRefreshActivity(activity, newConfig, lastReportedConfig)) { return; } final boolean cycleThroughStop = mWmService.mLetterboxConfiguration .isCameraCompatRefreshCycleThroughStopEnabled() && !activity.mLetterboxUiController .shouldRefreshActivityViaPauseForCameraCompat(); activity.mLetterboxUiController.setIsRefreshRequested(true); ProtoLog.v(WM_DEBUG_STATES, "Refreshing activity for freeform camera compatibility treatment, " + "activityRecord=%s", activity); final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain( activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); try { activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( activity.app.getThread(), refreshCallbackItem, resumeActivityItem); mHandler.postDelayed(() -> { synchronized (mWmService.mGlobalLock) { onActivityRefreshed(activity); } }, REFRESH_CALLBACK_TIMEOUT_MS); } catch (RemoteException e) { activity.mLetterboxUiController.setIsRefreshRequested(false); } } boolean isActivityRefreshing(@NonNull ActivityRecord activity) { return activity.mLetterboxUiController.isRefreshRequested(); } void onActivityRefreshed(@NonNull ActivityRecord activity) { // TODO(b/333060789): can we tell that refresh did not happen by observing the activity // state? activity.mLetterboxUiController.setIsRefreshRequested(false); } private boolean shouldRefreshActivity(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { return mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled() && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat() && ArrayUtils.find(mEvaluators.toArray(), evaluator -> ((Evaluator) evaluator) .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null; } /** * Interface for classes that would like to refresh the recently updated activity, based on the * configuration change. */ interface Evaluator { boolean shouldRefreshActivity(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig); } } services/core/java/com/android/server/wm/DisplayContent.java +5 −1 Original line number Diff line number Diff line Loading @@ -478,6 +478,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy; @Nullable final CameraStateMonitor mCameraStateMonitor; @Nullable final ActivityRefresher mActivityRefresher; DisplayFrames mDisplayFrames; final DisplayUpdater mDisplayUpdater; Loading Loading @@ -1233,13 +1235,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime(); if (shouldCreateDisplayRotationCompatPolicy) { mCameraStateMonitor = new CameraStateMonitor(this, mWmService.mH); mActivityRefresher = new ActivityRefresher(mWmService, mWmService.mH); mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy( this, mWmService.mH, mCameraStateMonitor); this, mCameraStateMonitor, mActivityRefresher); mCameraStateMonitor.startListeningToCameraState(); } else { // These are to satisfy the `final` check. mCameraStateMonitor = null; mActivityRefresher = null; mDisplayRotationCompatPolicy = null; } Loading Loading
data/etc/core.protolog.pb −38 B (52.8 KiB) File changed.No diff preview for this file type. View original file View changed file
data/etc/services.core.protolog.json +6 −6 Original line number Diff line number Diff line Loading @@ -583,6 +583,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, "7211222997110112110": { "message": "Refreshing activity for freeform camera compatibility treatment, activityRecord=%s", "level": "VERBOSE", "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRefresher.java" }, "1665699123574159131": { "message": "Starting activity when config will change = %b", "level": "VERBOSE", Loading Loading @@ -1771,12 +1777,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" }, "-7756685416834187936": { "message": "Refreshing activity for camera compatibility treatment, activityRecord=%s", "level": "VERBOSE", "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" }, "-5176775281239247368": { "message": "Reverting orientation after camera compat force rotation", "level": "VERBOSE", Loading
services/core/java/com/android/server/wm/ActivityRecord.java +7 −7 Original line number Diff line number Diff line Loading @@ -6530,8 +6530,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // and the token could be null. return; } if (r.mDisplayContent.mDisplayRotationCompatPolicy != null) { r.mDisplayContent.mDisplayRotationCompatPolicy.onActivityRefreshed(r); if (r.mDisplayContent.mActivityRefresher != null) { r.mDisplayContent.mActivityRefresher.onActivityRefreshed(r); } } Loading Loading @@ -9996,7 +9996,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo); } notifyDisplayCompatPolicyAboutConfigurationChange( notifyActivityRefresherAboutConfigurationChange( mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig); return true; } Loading Loading @@ -10063,18 +10063,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo); } notifyDisplayCompatPolicyAboutConfigurationChange( notifyActivityRefresherAboutConfigurationChange( mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig); return true; } private void notifyDisplayCompatPolicyAboutConfigurationChange( private void notifyActivityRefresherAboutConfigurationChange( Configuration newConfig, Configuration lastReportedConfig) { if (mDisplayContent.mDisplayRotationCompatPolicy == null if (mDisplayContent.mActivityRefresher == null || !shouldBeResumed(/* activeActivity */ null)) { return; } mDisplayContent.mDisplayRotationCompatPolicy.onActivityConfigurationChanging( mDisplayContent.mActivityRefresher.onActivityConfigurationChanging( this, newConfig, lastReportedConfig); } Loading
services/core/java/com/android/server/wm/ActivityRefresher.java 0 → 100644 +131 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.wm; import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import android.annotation.NonNull; import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; import android.content.res.Configuration; import android.os.Handler; import android.os.RemoteException; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; 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. */ class ActivityRefresher { // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The // client process may not always report the event back to the server, such as process is // crashed or got killed. private static final long REFRESH_CALLBACK_TIMEOUT_MS = 2000L; @NonNull private final WindowManagerService mWmService; @NonNull private final Handler mHandler; @NonNull private final ArrayList<Evaluator> mEvaluators = new ArrayList<>(); ActivityRefresher(@NonNull WindowManagerService wmService, @NonNull Handler handler) { mWmService = wmService; mHandler = handler; } void addEvaluator(@NonNull Evaluator evaluator) { mEvaluators.add(evaluator); } void removeEvaluator(@NonNull Evaluator evaluator) { mEvaluators.remove(evaluator); } /** * "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 onActivityConfigurationChanging(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { if (!shouldRefreshActivity(activity, newConfig, lastReportedConfig)) { return; } final boolean cycleThroughStop = mWmService.mLetterboxConfiguration .isCameraCompatRefreshCycleThroughStopEnabled() && !activity.mLetterboxUiController .shouldRefreshActivityViaPauseForCameraCompat(); activity.mLetterboxUiController.setIsRefreshRequested(true); ProtoLog.v(WM_DEBUG_STATES, "Refreshing activity for freeform camera compatibility treatment, " + "activityRecord=%s", activity); final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain( activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE); final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false); try { activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems( activity.app.getThread(), refreshCallbackItem, resumeActivityItem); mHandler.postDelayed(() -> { synchronized (mWmService.mGlobalLock) { onActivityRefreshed(activity); } }, REFRESH_CALLBACK_TIMEOUT_MS); } catch (RemoteException e) { activity.mLetterboxUiController.setIsRefreshRequested(false); } } boolean isActivityRefreshing(@NonNull ActivityRecord activity) { return activity.mLetterboxUiController.isRefreshRequested(); } void onActivityRefreshed(@NonNull ActivityRecord activity) { // TODO(b/333060789): can we tell that refresh did not happen by observing the activity // state? activity.mLetterboxUiController.setIsRefreshRequested(false); } private boolean shouldRefreshActivity(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) { return mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled() && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat() && ArrayUtils.find(mEvaluators.toArray(), evaluator -> ((Evaluator) evaluator) .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null; } /** * Interface for classes that would like to refresh the recently updated activity, based on the * configuration change. */ interface Evaluator { boolean shouldRefreshActivity(@NonNull ActivityRecord activity, @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig); } }
services/core/java/com/android/server/wm/DisplayContent.java +5 −1 Original line number Diff line number Diff line Loading @@ -478,6 +478,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy; @Nullable final CameraStateMonitor mCameraStateMonitor; @Nullable final ActivityRefresher mActivityRefresher; DisplayFrames mDisplayFrames; final DisplayUpdater mDisplayUpdater; Loading Loading @@ -1233,13 +1235,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime(); if (shouldCreateDisplayRotationCompatPolicy) { mCameraStateMonitor = new CameraStateMonitor(this, mWmService.mH); mActivityRefresher = new ActivityRefresher(mWmService, mWmService.mH); mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy( this, mWmService.mH, mCameraStateMonitor); this, mCameraStateMonitor, mActivityRefresher); mCameraStateMonitor.startListeningToCameraState(); } else { // These are to satisfy the `final` check. mCameraStateMonitor = null; mActivityRefresher = null; mDisplayRotationCompatPolicy = null; } Loading