Introduce process configuration to WindowProcessController
Introduced the process config and adjusted mergedconfiguration
related calls. Such that we can override configuration for a process
when need to.
The potential use cases include:
1. Maintain process window bounds for the latest activity to override
the display info for legacy apps;
2. Override the display info for IME process to make sure the IME can be
shown with the correct display metrics.
ActivityManagerService:
- Use process configuration instead of the global configuration when
it's for app.
ActivityStackSupervisor:
- Use process configuration when start activity.
WindowProcessController:
- Make it a ConfigurationContainer.
ActivityTaskManagerService:
- Add interface to get configuration for a process. If the process is a
system process or non-existing process, return the global
configuration.
- Return device configuration related to the process.
- Propagate configuration updates from Global to Process.
ActivityTaskManagerInternal:
- API to update configuration for IME process.
WindowManagerService/WindowManagerInternal:
- Propagate the process configuration change to wm.
WindowState:
- Use process configuration instead of global.
Test: go/wm-smoke
Test: servicestests will remain the same result as without this patch.
Bug: 113253755
Change-Id: I3660723352d2e8779d40528ae92d71f59ddbf1f1
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f7fe9e2..8c7fc84 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5895,7 +5895,8 @@
PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
- + processName + " with config " + getGlobalConfiguration());
+ + processName + " with config "
+ + app.getWindowProcessController().getConfiguration());
ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackage(appInfo);
@@ -6008,8 +6009,8 @@
instr2.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
- new Configuration(getGlobalConfiguration()), app.compat,
- getCommonServicesLocked(app.isolated),
+ new Configuration(app.getWindowProcessController().getConfiguration()),
+ app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
} else {
@@ -6017,8 +6018,8 @@
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
- new Configuration(getGlobalConfiguration()), app.compat,
- getCommonServicesLocked(app.isolated),
+ new Configuration(app.getWindowProcessController().getConfiguration()),
+ app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
}
@@ -8718,7 +8719,7 @@
StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
- final ProcessRecord r = new ProcessRecord(this, info, proc, uid);
+ final ProcessRecord r = new ProcessRecord(this, info, proc, uid, getGlobalConfiguration());
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
@@ -17082,30 +17083,6 @@
}
}
- // =========================================================
- // CONFIGURATION
- // =========================================================
-
- public ConfigurationInfo getDeviceConfigurationInfo() {
- ConfigurationInfo config = new ConfigurationInfo();
- synchronized (this) {
- final Configuration globalConfig = getGlobalConfiguration();
- config.reqTouchScreen = globalConfig.touchscreen;
- config.reqKeyboardType = globalConfig.keyboard;
- config.reqNavigation = globalConfig.navigation;
- if (globalConfig.navigation == Configuration.NAVIGATION_DPAD
- || globalConfig.navigation == Configuration.NAVIGATION_TRACKBALL) {
- config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
- }
- if (globalConfig.keyboard != Configuration.KEYBOARD_UNDEFINED
- && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
- config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
- }
- config.reqGlEsVersion = GL_ES_VERSION;
- }
- return config;
- }
-
@Override
public StackInfo getFocusedStackInfo() throws RemoteException {
return mActivityTaskManager.getFocusedStackInfo();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b24c36a..4bcaf71 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2063,7 +2063,12 @@
pw.print("has-secure-screen-lock: "); pw.println(kgm.isDeviceSecure());
}
- ConfigurationInfo configInfo = mInternal.getDeviceConfigurationInfo();
+ ConfigurationInfo configInfo = null;
+ try {
+ configInfo = mTaskInterface.getDeviceConfigurationInfo();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
if (protoOutputStream != null) {
protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c887370..1127db1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1558,7 +1558,8 @@
// we have to always create a new Configuration here.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
+ app.getWindowProcessController().getConfiguration(),
+ r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.icicle);
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 9acb04b..4dc2851 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -149,6 +149,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -649,6 +650,11 @@
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
}
+ int increaseConfigurationSeqLocked() {
+ mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
+ return mConfigurationSeq;
+ }
+
protected ActivityStackSupervisor createStackSupervisor() {
final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mH.getLooper());
supervisor.initialize();
@@ -704,6 +710,46 @@
return mLockTaskController;
}
+ /**
+ * Return the global configuration used by the process corresponding to the input pid. This is
+ * usually the global configuration with some overrides specific to that process.
+ */
+ Configuration getGlobalConfigurationForCallingPid() {
+ final int pid = Binder.getCallingPid();
+ if (pid == MY_PID || pid < 0) {
+ return getGlobalConfiguration();
+ }
+ synchronized (mGlobalLock) {
+ final WindowProcessController app = mPidMap.get(pid);
+ return app != null ? app.getConfiguration() : getGlobalConfiguration();
+ }
+ }
+
+ /**
+ * Return the device configuration info used by the process corresponding to the input pid.
+ * The value is consistent with the global configuration for the process.
+ */
+ @Override
+ public ConfigurationInfo getDeviceConfigurationInfo() {
+ ConfigurationInfo config = new ConfigurationInfo();
+ synchronized (mGlobalLock) {
+ final Configuration globalConfig = getGlobalConfigurationForCallingPid();
+ config.reqTouchScreen = globalConfig.touchscreen;
+ config.reqKeyboardType = globalConfig.keyboard;
+ config.reqNavigation = globalConfig.navigation;
+ if (globalConfig.navigation == Configuration.NAVIGATION_DPAD
+ || globalConfig.navigation == Configuration.NAVIGATION_TRACKBALL) {
+ config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+ }
+ if (globalConfig.keyboard != Configuration.KEYBOARD_UNDEFINED
+ && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
+ config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+ }
+ config.reqGlEsVersion = mAm.GL_ES_VERSION;
+ }
+ return config;
+ }
+
private void start() {
mInternal = new LocalService();
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
@@ -4264,7 +4310,7 @@
public Configuration getConfiguration() {
Configuration ci;
synchronized(mGlobalLock) {
- ci = new Configuration(getGlobalConfiguration());
+ ci = new Configuration(getGlobalConfigurationForCallingPid());
ci.userSetLocale = false;
}
return ci;
@@ -4420,8 +4466,7 @@
locales.get(bestLocaleIndex)));
}
- mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
- mTempConfig.seq = mConfigurationSeq;
+ mTempConfig.seq = increaseConfigurationSeqLocked();
// Update stored global config and notify everyone about the change.
mStackSupervisor.onConfigurationChanged(mTempConfig);
@@ -4455,6 +4500,7 @@
mAm.mHandler.sendMessage(msg);
}
+ // TODO: Consider using mPidMap to update configurations for processes.
for (int i = mAm.mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mAm.mLruProcesses.get(i);
try {
@@ -5596,5 +5642,47 @@
}
}
+ /**
+ * Set the corresponding display information for the process global configuration. To be
+ * called when we need to show IME on a different display.
+ *
+ * @param pid The process id associated with the IME window.
+ * @param displayId The ID of the display showing the IME.
+ */
+ @Override
+ public void onImeWindowSetOnDisplay(int pid, int displayId) {
+ if (pid == MY_PID || pid < 0) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG,
+ "Trying to update display configuration for system/invalid process.");
+ }
+ return;
+ }
+ mH.post(() -> {
+ synchronized (mGlobalLock) {
+ // Check if display is initialized in AM.
+ if (!mStackSupervisor.isDisplayAdded(displayId)) {
+ // Call come when display is not yet added or has already been removed.
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG, "Trying to update display configuration for non-existing "
+ + "displayId=" + displayId);
+ }
+ return;
+ }
+ final WindowProcessController imeProcess = mPidMap.get(pid);
+ if (imeProcess == null) {
+ if (DEBUG_CONFIGURATION) {
+ Slog.w(TAG, "Trying to update display configuration for invalid pid: "
+ + pid);
+ }
+ return;
+ }
+ // Fetch the current override configuration of the display and set it to the
+ // process global configuration.
+ imeProcess.onConfigurationChanged(
+ mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+ }
+ });
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index d3dc0f3..667d3fa 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -17,18 +17,10 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import android.os.Debug;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.util.EventLog;
-import android.util.Slog;
-import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.app.procstats.ProcessState;
-import com.android.internal.os.BatteryStatsImpl;
-
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.IApplicationThread;
@@ -36,7 +28,9 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
import android.os.Binder;
+import android.os.Debug;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -44,10 +38,18 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.EventLog;
+import android.util.Slog;
import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.app.procstats.ProcessState;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BatteryStatsImpl;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -307,6 +309,7 @@
pw.print(prefix); pw.print("manageSpaceActivityName=");
pw.println(info.manageSpaceActivityName);
}
+
pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir);
pw.print(" publicDir="); pw.print(info.publicSourceDir);
pw.print(" data="); pw.println(info.dataDir);
@@ -520,7 +523,7 @@
}
ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
- int _uid) {
+ int _uid, Configuration config) {
mService = _service;
info = _info;
isolated = _info.uid != _uid;
@@ -534,7 +537,7 @@
removed = false;
lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
mWindowProcessController = new WindowProcessController(
- mService.mActivityTaskManager, info, processName, uid, userId, this, this);
+ mService.mActivityTaskManager, info, processName, uid, userId, this, this, config);
pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
}
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index e5551b5..da172fb 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -17,7 +17,10 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -29,11 +32,12 @@
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import android.app.Activity;
-import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.IApplicationThread;
+import android.app.servertransaction.ConfigurationChangeItem;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
@@ -41,7 +45,7 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.util.function.pooled.PooledRunnable;
+import com.android.server.wm.ConfigurationContainer;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -57,9 +61,10 @@
* window manager so the window manager lock is held and appropriate permissions are checked before
* calls are allowed to proceed.
*/
-public class WindowProcessController {
+public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer> {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_AM;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+ private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
// all about the first app in the process
final ApplicationInfo mInfo;
@@ -108,8 +113,12 @@
// any tasks this process had run root activities in
private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<>();
+ // Last configuration that was reported to the process.
+ private final Configuration mLastReportedConfiguration;
+
WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
- int uid, int userId, Object owner, WindowProcessListener listener) {
+ int uid, int userId, Object owner, WindowProcessListener listener,
+ Configuration config) {
mInfo = info;
mName = name;
mUid = uid;
@@ -117,6 +126,10 @@
mOwner = owner;
mListener = listener;
mAtm = atm;
+ mLastReportedConfiguration = new Configuration();
+ if (config != null) {
+ onConfigurationChanged(config);
+ }
}
public void setPid(int pid) {
@@ -219,6 +232,21 @@
return mInstrumenting;
}
+ @Override
+ protected int getChildCount() {
+ return 0;
+ }
+
+ @Override
+ protected ConfigurationContainer getChildAt(int index) {
+ return null;
+ }
+
+ @Override
+ protected ConfigurationContainer getParent() {
+ return null;
+ }
+
public void addPackage(String packageName) {
synchronized (mAtm.mGlobalLock) {
mPkgList.add(packageName);
@@ -526,6 +554,50 @@
mAtm.mH.post(r);
}
+ @Override
+ public void onConfigurationChanged(Configuration newGlobalConfig) {
+ super.onConfigurationChanged(newGlobalConfig);
+ updateConfiguration();
+ }
+
+ @Override
+ public void onOverrideConfigurationChanged(Configuration newOverrideConfig) {
+ super.onOverrideConfigurationChanged(newOverrideConfig);
+ updateConfiguration();
+ }
+
+ private void updateConfiguration() {
+ final Configuration config = getConfiguration();
+ if (mLastReportedConfiguration.diff(config) == 0) {
+ // Nothing changed.
+ return;
+ }
+
+ try {
+ if (mThread == null) {
+ return;
+ }
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName
+ + " new config " + config);
+ }
+ config.seq = mAtm.increaseConfigurationSeqLocked();
+ mAtm.getLifecycleManager().scheduleTransaction(mThread,
+ ConfigurationChangeItem.obtain(config));
+ setLastReportedConfiguration(config);
+ } catch (Exception e) {
+ Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
+ }
+ }
+
+ private void setLastReportedConfiguration(Configuration config) {
+ mLastReportedConfiguration.setTo(config);
+ }
+
+ Configuration getLastReportedConfiguration() {
+ return mLastReportedConfiguration;
+ }
+
/** Returns the total time (in milliseconds) spent executing in both user and system code. */
public long getCpuTime() {
return (mListener != null) ? mListener.getCpuTime() : 0;
@@ -574,6 +646,9 @@
pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
}
}
+ pw.println(prefix + " Configuration=" + getConfiguration());
+ pw.println(prefix + " OverrideConfiguration=" + getOverrideConfiguration());
+ pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
}
}