Initial implementation of system service optimizations for different type of users.
On R, we want to optimize boot time by not starting system services for some types of users,
like a headless system user (which is the case for Automotive)
As a "guinea pig", it optimizes VoiceInteractionService for headless system user, so the 3rd-party app
service is not bound for user 0 (and hence its process is not launched).
This change improves boot time on Automotive in about 100ms.
Test: atest CtsVoiceInteractionTestCases CtsAssistTestCases # on walleye and Automotive
Test: manual verification on logcat
Bug: 133242016
Fixes: 137878080
Change-Id: Ib0a902855e32691a1d00bfa77ee82c8e2430977c
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 8e9e74e..4facf4ea 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -18,12 +18,17 @@
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.ServiceManager;
import android.os.UserManager;
+import com.android.server.pm.UserManagerService;
+
/**
* The base class for services running in the system process. Override and implement
* the lifecycle event callback methods as needed.
@@ -145,11 +150,29 @@
public void onBootPhase(int phase) {}
/**
+ * @deprecated subclasses should extend {@link #onStartUser(int, int)} instead (which by default
+ * calls this method).
+ */
+ @Deprecated
+ public void onStartUser(@UserIdInt int userHandle) {}
+
+ /**
* Called when a new user is starting, for system services to initialize any per-user
* state they maintain for running users.
- * @param userHandle The identifier of the user.
+ *
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onStartUser(int userHandle) {}
+ public void onStartUser(@NonNull UserInfo userInfo) {
+ onStartUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onUnlockUser(int, int)} instead (which by
+ * default calls this method).
+ */
+ @Deprecated
+ public void onUnlockUser(@UserIdInt int userHandle) {}
/**
* Called when an existing user is in the process of being unlocked. This
@@ -163,17 +186,38 @@
* {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
* these states.
*
- * @param userHandle The identifier of the user.
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onUnlockUser(int userHandle) {}
+ public void onUnlockUser(@NonNull UserInfo userInfo) {
+ onUnlockUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onSwitchUser(int, int)} instead (which by
+ * default calls this method).
+ */
+ @Deprecated
+ public void onSwitchUser(@UserIdInt int userHandle) {}
/**
* Called when switching to a different foreground user, for system services that have
* special behavior for whichever user is currently in the foreground. This is called
* before any application processes are aware of the new user.
- * @param userHandle The identifier of the user.
+ *
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onSwitchUser(int userHandle) {}
+ public void onSwitchUser(@NonNull UserInfo userInfo) {
+ onSwitchUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onStopUser(int, int)} instead (which by default
+ * calls this method).
+ */
+ @Deprecated
+ public void onStopUser(@UserIdInt int userHandle) {}
/**
* Called when an existing user is stopping, for system services to finalize any per-user
@@ -183,9 +227,19 @@
*
* <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
*
- * @param userHandle The identifier of the user.
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onStopUser(int userHandle) {}
+ public void onStopUser(@NonNull UserInfo userInfo) {
+ onStopUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onCleanupUser(int, int)} instead (which by
+ * default calls this method).
+ */
+ @Deprecated
+ public void onCleanupUser(@UserIdInt int userHandle) {}
/**
* Called when an existing user is stopping, for system services to finalize any per-user
@@ -193,11 +247,14 @@
* teardown of the user is complete.
*
* <p>NOTE: When this callback is called, the CE storage for the target user may not be
- * accessible already. Use {@link #onStopUser} instead if you need to access the CE storage.
+ * accessible already. Use {@link #onCleanupUser} instead if you need to access the CE storage.
*
- * @param userHandle The identifier of the user.
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onCleanupUser(int userHandle) {}
+ public void onCleanupUser(@NonNull UserInfo userInfo) {
+ onCleanupUser(userInfo.id);
+ }
/**
* Publish the service so it is accessible to other services and apps.
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 9711152..ebe23f6 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -19,9 +19,11 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.UserManagerInternal;
import android.util.Slog;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -53,6 +55,8 @@
private int mCurrentPhase = -1;
+ private UserManagerInternal mUserManagerInternal;
+
SystemServiceManager(Context context) {
mContext = context;
}
@@ -187,11 +191,30 @@
}
/**
+ * Called at the beginning of {@code ActivityManagerService.systemReady()}.
+ */
+ public void preSystemReady() {
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ }
+
+ private @NonNull UserInfo getUserInfo(@UserIdInt int userHandle) {
+ if (mUserManagerInternal == null) {
+ throw new IllegalStateException("mUserManagerInternal not set yet");
+ }
+ final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle);
+ if (userInfo == null) {
+ throw new IllegalStateException("No UserInfo for " + userHandle);
+ }
+ return userInfo;
+ }
+
+ /**
* Starts the given user.
*/
- public void startUser(@NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
+ public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
t.traceBegin("ssm.startUser-" + userHandle);
Slog.i(TAG, "Calling onStartUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -199,7 +222,7 @@
t.traceBegin("onStartUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onStartUser(userHandle);
+ service.onStartUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+ " to service " + service.getClass().getName(), ex);
@@ -217,6 +240,7 @@
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.unlockUser-" + userHandle);
Slog.i(TAG, "Calling onUnlockUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -224,7 +248,7 @@
t.traceBegin("onUnlockUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onUnlockUser(userHandle);
+ service.onUnlockUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
+ " to service " + serviceName, ex);
@@ -242,6 +266,7 @@
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.switchUser-" + userHandle);
Slog.i(TAG, "Calling switchUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -249,7 +274,7 @@
t.traceBegin("onSwitchUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onSwitchUser(userHandle);
+ service.onSwitchUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+ " to service " + serviceName, ex);
@@ -267,6 +292,7 @@
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.stopUser-" + userHandle);
Slog.i(TAG, "Calling onStopUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -274,7 +300,7 @@
t.traceBegin("onStopUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onStopUser(userHandle);
+ service.onStopUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+ " to service " + serviceName, ex);
@@ -292,6 +318,7 @@
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.cleanupUser-" + userHandle);
Slog.i(TAG, "Calling onCleanupUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -299,7 +326,7 @@
t.traceBegin("onCleanupUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onCleanupUser(userHandle);
+ service.onCleanupUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+ " to service " + serviceName, ex);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ede573a..4f2cddd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8990,6 +8990,7 @@
*/
public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
t.traceBegin("PhaseActivityManagerReady");
+ mSystemServiceManager.preSystemReady();
synchronized(this) {
if (mSystemReady) {
// If we're done calling all the receivers, run the next "boot phase" passed in
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2ccb6c1..ead9d7a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3762,6 +3762,8 @@
pw.print(" <partial>");
}
pw.println();
+ pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" (");
+ pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")");
pw.print(" State: ");
final int state;
synchronized (mUserStates) {
@@ -3846,6 +3848,8 @@
pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_guestUserEphemeral));
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
+ pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
+ pw.println(" User version: " + mUserVersion);
}
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
@@ -4172,6 +4176,14 @@
Bundle restrictions = getEffectiveUserRestrictions(userId);
return restrictions != null && restrictions.getBoolean(restrictionKey);
}
+
+ public @Nullable UserInfo getUserInfo(@UserIdInt int userId) {
+ UserData userData;
+ synchronized (mUsersLock) {
+ userData = mUsers.get(userId);
+ }
+ return userData == null ? null : userData.info;
+ }
}
/* Remove all the users except of the system one. */