diff options
| author | 2019-08-13 09:28:33 -0700 | |
|---|---|---|
| committer | 2019-08-13 10:10:53 -0700 | |
| commit | e5434c301ba960ba11afa6552ca65799051705a5 (patch) | |
| tree | 2c36aeed6b84423285a53e6ac1cb5cfd7d37fb58 | |
| parent | 1a919322f14cada8cb7f6da7d1b72daebc16c03e (diff) | |
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
7 files changed, 220 insertions, 26 deletions
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 1c37c64bb622..fcdc81cf7533 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -16,11 +16,17 @@ package android.content.pm; +import android.annotation.IntDef; import android.annotation.UnsupportedAppUsage; +import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; import android.os.UserManager; +import android.util.DebugUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Per-user information. @@ -119,10 +125,31 @@ public class UserInfo implements Parcelable { */ public static final int FLAG_SYSTEM = 0x00000800; + /** + * @hide + */ + @IntDef(flag = true, prefix = "FLAG_", value = { + FLAG_PRIMARY, + FLAG_ADMIN, + FLAG_GUEST, + FLAG_RESTRICTED, + FLAG_INITIALIZED, + FLAG_MANAGED_PROFILE, + FLAG_DISABLED, + FLAG_QUIET_MODE, + FLAG_EPHEMERAL, + FLAG_DEMO, + FLAG_FULL, + FLAG_SYSTEM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UserInfoFlag { + } + public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL; @UnsupportedAppUsage - public int id; + public @UserIdInt int id; @UnsupportedAppUsage public int serialNumber; @UnsupportedAppUsage @@ -130,7 +157,7 @@ public class UserInfo implements Parcelable { @UnsupportedAppUsage public String iconPath; @UnsupportedAppUsage - public int flags; + public @UserInfoFlag int flags; @UnsupportedAppUsage public long creationTime; @UnsupportedAppUsage @@ -214,6 +241,10 @@ public class UserInfo implements Parcelable { return (flags & FLAG_DEMO) == FLAG_DEMO; } + public boolean isFull() { + return (flags & FLAG_FULL) == FLAG_FULL; + } + /** * Returns true if the user is a split system user. * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled, @@ -290,13 +321,23 @@ public class UserInfo implements Parcelable { @Override public String toString() { + // NOTE: do not change this string, it's used by 'pm list users', which in turn is + // used and parsed by TestDevice. In other words, if you change it, you'd have to change + // TestDevice, TestDeviceTest, and possibly others.... return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}"; } + /** @hide */ + public static String flagsToString(int flags) { + return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags); + } + + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(id); dest.writeString(name); diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java index 2e3b000f2b39..f302263f23ce 100644 --- a/core/java/android/os/UserManagerInternal.java +++ b/core/java/android/os/UserManagerInternal.java @@ -16,6 +16,7 @@ package android.os; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -224,4 +225,10 @@ public abstract class UserManagerInternal { /** @return a specific user restriction that's in effect currently. */ public abstract boolean hasUserRestriction(String restriction, int userId); + + /** + * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not + * found. + */ + public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId); } diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 8e9e74ecedbf..4facf4ea62a2 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -18,12 +18,17 @@ package com.android.server; 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 abstract class SystemService { 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(@NonNull UserInfo userInfo) { + onStartUser(userInfo.id); + } + + /** + * @deprecated subclasses should extend {@link #onUnlockUser(int, int)} instead (which by + * default calls this method). */ - public void onStartUser(int userHandle) {} + @Deprecated + public void onUnlockUser(@UserIdInt int userHandle) {} /** * Called when an existing user is in the process of being unlocked. This @@ -163,17 +186,38 @@ public abstract class SystemService { * {@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(@NonNull UserInfo userInfo) { + onSwitchUser(userInfo.id); + } + + /** + * @deprecated subclasses should extend {@link #onStopUser(int, int)} instead (which by default + * calls this method). */ - public void onSwitchUser(int userHandle) {} + @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 @@ public abstract class SystemService { * * <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 @@ public abstract class SystemService { * 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 9711152ec5b1..ebe23f61cb2a 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -19,9 +19,11 @@ package com.android.server; 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 @@ public class SystemServiceManager { private int mCurrentPhase = -1; + private UserManagerInternal mUserManagerInternal; + SystemServiceManager(Context context) { mContext = context; } @@ -187,11 +191,30 @@ public class SystemServiceManager { } /** + * 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 @@ public class SystemServiceManager { 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 ede573a64c65..4f2cddd4ebfd 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 class ActivityManagerService extends IActivityManager.Stub */ 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 2ccb6c172a28..ead9d7ac2e56 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { 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. */ diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index b2fde548e506..b2ac5490440d 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; @@ -36,6 +37,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.ShortcutServiceInternal; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.soundtrigger.IRecognitionStatusCallback; @@ -80,6 +82,7 @@ import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.soundtrigger.SoundTriggerInternal; +import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; @@ -92,7 +95,7 @@ import java.util.concurrent.Executor; */ public class VoiceInteractionManagerService extends SystemService { static final String TAG = "VoiceInteractionManagerService"; - static final boolean DEBUG = false; + static final boolean DEBUG = true; // TODO(b/133242016) STOPSHIP: change to false before R ships final Context mContext; final ContentResolver mResolver; @@ -154,19 +157,37 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void onStartUser(int userHandle) { - mServiceStub.initForUser(userHandle); + public void onStartUser(@NonNull UserInfo userInfo) { + if (DEBUG) Slog.d(TAG, "onStartUser(" + userInfo + ")"); + + if (!userInfo.isFull()) { + if (DEBUG) Slog.d(TAG, "***** skipping on non-full user " + userInfo); + return; + } + mServiceStub.initForUser(userInfo.id); } @Override - public void onUnlockUser(int userHandle) { - mServiceStub.initForUser(userHandle); + public void onUnlockUser(@NonNull UserInfo userInfo) { + if (DEBUG) Slog.d(TAG, "onUnlockUser(" + userInfo + ")"); + + if (!userInfo.isFull()) { + if (DEBUG) Slog.d(TAG, "***** skipping on non-full user " + userInfo); + return; + } + mServiceStub.initForUser(userInfo.id); mServiceStub.switchImplementationIfNeeded(false); } @Override - public void onSwitchUser(int userHandle) { - mServiceStub.switchUser(userHandle); + public void onSwitchUser(@NonNull UserInfo userInfo) { + if (DEBUG) Slog.d(TAG, "onSwitchUser(" + userInfo + ")"); + + if (!userInfo.isFull()) { + if (DEBUG) Slog.d(TAG, "***** skipping on non-full user " + userInfo); + return; + } + mServiceStub.switchUser(userInfo.id); } class LocalService extends VoiceInteractionManagerInternal { @@ -270,6 +291,20 @@ public class VoiceInteractionManagerService extends SystemService { } public void initForUser(int userHandle) { + final TimingsTraceAndSlog t; + if (DEBUG) { + t = TimingsTraceAndSlog.newAsyncLog(); + t.traceBegin("VoiceInteractionSvc.initForUser(" + userHandle + ")"); + } else { + t = null; + } + initForUserNoTracing(userHandle); + if (t != null) { + t.traceEnd(); + } + } + + private void initForUserNoTracing(@UserIdInt int userHandle) { if (DEBUG) Slog.d(TAG, "**************** initForUser user=" + userHandle); String curInteractorStr = Settings.Secure.getStringForUser( mContext.getContentResolver(), @@ -426,6 +461,20 @@ public class VoiceInteractionManagerService extends SystemService { } void switchImplementationIfNeededLocked(boolean force) { + final TimingsTraceAndSlog t; + if (DEBUG) { + t = TimingsTraceAndSlog.newAsyncLog(); + t.traceBegin("VoiceInteractionSvc.switchImplementation(" + mCurUser + ")"); + } else { + t = null; + } + switchImplementationIfNeededNoTracingLocked(force); + if (t != null) { + t.traceEnd(); + } + } + + void switchImplementationIfNeededNoTracingLocked(boolean force) { if (!mSafeMode) { String curService = Settings.Secure.getStringForUser( mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser); |