Merge changes Ibd113f4f,I09c2feff into tm-qpr-dev am: 9605ca616b am: 37fa423c02

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18368523

Change-Id: I1cf8c3d22795c206e809bb986ebc73009e6a3bcc
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Riddle Hsu
2022-05-16 04:06:24 +00:00
committed by Automerger Merge Worker
6 changed files with 112 additions and 121 deletions

View File

@@ -3413,26 +3413,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.
@@ -3449,22 +3435,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. */

View File

@@ -32,8 +32,6 @@ interface ActivityThreadInternal {
boolean isInDensityCompatMode();
boolean isCachedProcessState();
Application getApplication();
ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts);

View File

@@ -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);

View File

@@ -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;
@@ -570,53 +569,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());

View File

@@ -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) {

View File

@@ -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;
@@ -281,6 +288,39 @@ public class WindowProcessControllerTests extends WindowTestsBase {
mWpc.getConfiguration().seq, globalSeq);
}
@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);