diff options
| author | 2022-05-16 03:12:44 +0000 | |
|---|---|---|
| committer | 2022-05-16 03:12:44 +0000 | |
| commit | 9605ca616b8c0eb937e2fc769b67a31f1d937d87 (patch) | |
| tree | 4bf984aecb2e2181178313d6f32d4d7a681a73b8 | |
| parent | 15f33649a5df94cec4152710125a37fd55290cb0 (diff) | |
| parent | 7371d0be415827866b6c43c17b526930a9dd6362 (diff) | |
Merge changes Ibd113f4f,I09c2feff into tm-qpr-dev
* changes:
Remove client side deferred config for cached state
Defer sending config change to cached process
6 files changed, 112 insertions, 121 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b4cabada0522..98683f0bd87a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3410,26 +3410,12 @@ public final class ActivityThread extends ClientTransactionHandler } } - /** - * Returns {@code true} if the {@link android.app.ActivityManager.ProcessState} of the current - * process is cached. - */ - @Override - @VisibleForTesting - public boolean isCachedProcessState() { - synchronized (mAppThread) { - return mLastProcessState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; - } - } - @Override public void updateProcessState(int processState, boolean fromIpc) { - final boolean wasCached; synchronized (mAppThread) { if (mLastProcessState == processState) { return; } - wasCached = isCachedProcessState(); mLastProcessState = processState; // Defer the top state for VM to avoid aggressive JIT compilation affecting activity // launch time. @@ -3446,22 +3432,6 @@ public final class ActivityThread extends ClientTransactionHandler + (fromIpc ? " (from ipc" : "")); } } - - // Handle the pending configuration if the process state is changed from cached to - // non-cached. Except the case where there is a launching activity because the - // LaunchActivityItem will handle it. - if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) { - final Configuration pendingConfig = - mConfigurationController.getPendingConfiguration(false /* clearPending */); - if (pendingConfig == null) { - return; - } - if (Looper.myLooper() == mH.getLooper()) { - handleConfigurationChanged(pendingConfig); - } else { - sendMessage(H.CONFIGURATION_CHANGED, pendingConfig); - } - } } /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */ diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java index b9ad5c337813..72506b9fcdbb 100644 --- a/core/java/android/app/ActivityThreadInternal.java +++ b/core/java/android/app/ActivityThreadInternal.java @@ -32,8 +32,6 @@ interface ActivityThreadInternal { boolean isInDensityCompatMode(); - boolean isCachedProcessState(); - Application getApplication(); ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts); diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index 1a77b65c8ef6..18dc1ce18baf 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -124,12 +124,6 @@ class ConfigurationController { * @param config The new configuration. */ void handleConfigurationChanged(@NonNull Configuration config) { - if (mActivityThread.isCachedProcessState()) { - updatePendingConfiguration(config); - // If the process is in a cached state, delay the handling until the process is no - // longer cached. - return; - } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged"); handleConfigurationChanged(config, null /* compat */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index bfb2fd57975f..a2d4bafef41c 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -31,7 +31,6 @@ import static org.junit.Assert.assertTrue; import android.annotation.Nullable; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ActivityThread.ActivityClientRecord; import android.app.IApplicationThread; @@ -571,53 +570,6 @@ public class ActivityThreadTest { } @Test - public void testHandleProcessConfigurationChanged_DependOnProcessState() { - final ActivityThread activityThread = ActivityThread.currentActivityThread(); - final Configuration origConfig = activityThread.getConfiguration(); - final int newDpi = origConfig.densityDpi + 10; - final Configuration newConfig = new Configuration(origConfig); - newConfig.seq++; - newConfig.densityDpi = newDpi; - - activityThread.updateProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, - false /* fromIPC */); - - applyProcessConfiguration(activityThread, newConfig); - try { - // In the cached state, the configuration is only set as pending and not applied. - assertEquals(origConfig.densityDpi, activityThread.getConfiguration().densityDpi); - assertTrue(activityThread.isCachedProcessState()); - } finally { - // The foreground state is the default state of instrumentation. - activityThread.updateProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, - false /* fromIPC */); - } - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - - try { - // The state becomes non-cached, the pending configuration should be applied. - assertEquals(newConfig.densityDpi, activityThread.getConfiguration().densityDpi); - assertFalse(activityThread.isCachedProcessState()); - } finally { - // Restore to the original configuration. - activityThread.getConfiguration().seq = origConfig.seq - 1; - applyProcessConfiguration(activityThread, origConfig); - } - } - - private static void applyProcessConfiguration(ActivityThread thread, Configuration config) { - final ClientTransaction clientTransaction = newTransaction(thread, - null /* activityToken */); - clientTransaction.addCallback(ConfigurationChangeItem.obtain(config)); - final IApplicationThread appThread = thread.getApplicationThread(); - try { - appThread.scheduleTransaction(clientTransaction); - } catch (Exception ignored) { - } - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - } - - @Test public void testResumeAfterNewIntent() { final Activity activity = mActivityTestRule.launchActivity(new Intent()); final ActivityThread activityThread = activity.getActivityThread(); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 40417a4857d3..1c64a06036f0 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED; @@ -195,6 +196,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether the process configuration is waiting to be dispatched to the process. */ private boolean mHasPendingConfigurationChange; + /** If the process state is in (<=) the cached state, then defer delivery of the config. */ + private static final int CACHED_CONFIG_PROC_STATE = PROCESS_STATE_CACHED_ACTIVITY; + /** Whether {@link #mLastReportedConfiguration} is deferred by the cached state. */ + private volatile boolean mHasCachedConfiguration; + /** * Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not * registered. @@ -316,8 +322,27 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mCurProcState; } + /** + * Sets the computed process state from the oom adjustment calculation. This is frequently + * called in activity manager's lock, so don't use window manager lock here. + */ + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public void setReportedProcState(int repProcState) { + final int prevProcState = mRepProcState; mRepProcState = repProcState; + + // Deliver the cached config if the app changes from cached state to non-cached state. + final IApplicationThread thread = mThread; + if (prevProcState >= CACHED_CONFIG_PROC_STATE && repProcState < CACHED_CONFIG_PROC_STATE + && thread != null && mHasCachedConfiguration) { + final Configuration config; + synchronized (mLastReportedConfiguration) { + config = new Configuration(mLastReportedConfiguration); + } + // Schedule immediately to make sure the app component (e.g. receiver, service) can get + // the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate). + scheduleConfigurationChange(thread, config); + } } int getReportedProcState() { @@ -1328,12 +1353,22 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @Override public void onConfigurationChanged(Configuration newGlobalConfig) { super.onConfigurationChanged(newGlobalConfig); - updateConfiguration(); - } + final Configuration config = getConfiguration(); + if (mLastReportedConfiguration.equals(config)) { + // Nothing changed. + if (Build.IS_DEBUGGABLE && mHasImeService) { + // TODO (b/135719017): Temporary log for debugging IME service. + Slog.w(TAG_CONFIGURATION, "Current config: " + config + + " unchanged for IME proc " + mName); + } + return; + } - @Override - public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { - super.onRequestedOverrideConfigurationChanged(overrideConfiguration); + if (mPauseConfigurationDispatchCount > 0) { + mHasPendingConfigurationChange = true; + return; + } + dispatchConfiguration(config); } @Override @@ -1359,25 +1394,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio resolvedConfig.seq = newParentConfig.seq; } - private void updateConfiguration() { - final Configuration config = getConfiguration(); - if (mLastReportedConfiguration.diff(config) == 0) { - // Nothing changed. - if (Build.IS_DEBUGGABLE && mHasImeService) { - // TODO (b/135719017): Temporary log for debugging IME service. - Slog.w(TAG_CONFIGURATION, "Current config: " + config - + " unchanged for IME proc " + mName); - } - return; - } - - if (mPauseConfigurationDispatchCount > 0) { - mHasPendingConfigurationChange = true; - return; - } - dispatchConfiguration(config); - } - void dispatchConfiguration(Configuration config) { mHasPendingConfigurationChange = false; if (mThread == null) { @@ -1388,29 +1404,47 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } return; } + + config.seq = mAtm.increaseConfigurationSeqLocked(); + setLastReportedConfiguration(config); + + // A cached process doesn't have running application components, so it is unnecessary to + // notify the configuration change. The last-reported-configuration is still set because + // setReportedProcState() should not write any fields that require WM lock. + if (mRepProcState >= CACHED_CONFIG_PROC_STATE) { + mHasCachedConfiguration = true; + // Because there are 2 volatile accesses in setReportedProcState(): mRepProcState and + // mHasCachedConfiguration, check again in case mRepProcState is changed but hasn't + // read the change of mHasCachedConfiguration. + if (mRepProcState >= CACHED_CONFIG_PROC_STATE) { + return; + } + } + + scheduleConfigurationChange(mThread, config); + } + + private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, config); if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); } - + mHasCachedConfiguration = false; try { - config.seq = mAtm.increaseConfigurationSeqLocked(); - mAtm.getLifecycleManager().scheduleTransaction(mThread, + mAtm.getLifecycleManager().scheduleTransaction(thread, ConfigurationChangeItem.obtain(config)); - setLastReportedConfiguration(config); } catch (Exception e) { - Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e); + Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e); } } void setLastReportedConfiguration(Configuration config) { - mLastReportedConfiguration.setTo(config); - } - - Configuration getLastReportedConfiguration() { - return mLastReportedConfiguration; + // Synchronize for the access from setReportedProcState(). + synchronized (mLastReportedConfiguration) { + mLastReportedConfiguration.setTo(config); + } } void pauseConfigurationDispatch() { @@ -1461,6 +1495,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // config seq. This increment ensures that the client won't ignore the configuration. config.seq = mAtm.increaseConfigurationSeqLocked(); } + // LaunchActivityItem includes the latest process configuration. + mHasCachedConfiguration = false; return config; } @@ -1688,7 +1724,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } pw.println(prefix + " Configuration=" + getConfiguration()); pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration()); - pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration); + pw.println(prefix + " mLastReportedConfiguration=" + (mHasCachedConfiguration + ? ("(cached) " + mLastReportedConfiguration) : mLastReportedConfiguration)); final int stateFlags = mActivityStateFlags; if (stateFlags != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 746f2b50ac3a..3abf7ce665ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 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.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -39,24 +40,30 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.Manifest; +import android.app.ActivityManager; +import android.app.ClientTransactionHandler; import android.app.IApplicationThread; +import android.app.servertransaction.ConfigurationChangeItem; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.LocaleList; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mockito; @@ -282,6 +289,39 @@ public class WindowProcessControllerTests extends WindowTestsBase { } @Test + public void testCachedStateConfigurationChange() throws RemoteException { + final ClientLifecycleManager clientManager = mAtm.getLifecycleManager(); + doNothing().when(clientManager).scheduleTransaction(any(), any()); + final IApplicationThread thread = mWpc.getThread(); + final Configuration newConfig = new Configuration(mWpc.getConfiguration()); + newConfig.densityDpi += 100; + // Non-cached state will send the change directly. + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + clearInvocations(clientManager); + mWpc.onConfigurationChanged(newConfig); + verify(clientManager).scheduleTransaction(eq(thread), any()); + + // Cached state won't send the change. + clearInvocations(clientManager); + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + newConfig.densityDpi += 100; + mWpc.onConfigurationChanged(newConfig); + verify(clientManager, never()).scheduleTransaction(eq(thread), any()); + + // Cached -> non-cached will send the previous deferred config immediately. + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER); + final ArgumentCaptor<ConfigurationChangeItem> captor = + ArgumentCaptor.forClass(ConfigurationChangeItem.class); + verify(clientManager).scheduleTransaction(eq(thread), captor.capture()); + final ClientTransactionHandler client = mock(ClientTransactionHandler.class); + captor.getValue().preExecute(client, null /* token */); + final ArgumentCaptor<Configuration> configCaptor = + ArgumentCaptor.forClass(Configuration.class); + verify(client).updatePendingConfiguration(configCaptor.capture()); + assertEquals(newConfig, configCaptor.getValue()); + } + + @Test public void testComputeOomAdjFromActivities() { final ActivityRecord activity = createActivityRecord(mWpc); activity.mVisibleRequested = true; |