Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a68d5351 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Defined a more generic way to let services "opt-out" from certain user types."

parents bcc0b1c2 501a514b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -327,6 +327,14 @@ public class UserInfo implements Parcelable {
        return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
    }

    /** @hide */
    public String toFullString() {
        return "UserInfo[id=" + id
                + ", name=" + name
                + ", flags=" + flagsToString(flags)
                + "]";
    }

    /** @hide */
    public static String flagsToString(int flags) {
        return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
+43 −10
Original line number Diff line number Diff line
@@ -54,6 +54,10 @@ import com.android.server.pm.UserManagerService;
 * {@hide}
 */
public abstract class SystemService {

    // TODO(b/133242016) STOPSHIP: change to false before R ships
    protected static final boolean DEBUG_USER = true;

    /*
     * Boot Phases
     */
@@ -150,7 +154,17 @@ public abstract class SystemService {
    public void onBootPhase(int phase) {}

    /**
     * @deprecated subclasses should extend {@link #onStartUser(int, int)} instead (which by default
     * Checks if the service should be available for the given user.
     *
     * <p>By default returns {@code true}, but subclasses should extend for optimization, if they
     * don't support some types (like headless system user).
     */
    public boolean isSupported(@NonNull UserInfo userInfo) {
        return true;
    }

    /**
     * @deprecated subclasses should extend {@link #onStartUser(UserInfo)} instead (which by default
     * calls this method).
     */
    @Deprecated
@@ -160,6 +174,9 @@ public abstract class SystemService {
     * Called when a new user is starting, for system services to initialize any per-user
     * state they maintain for running users.
     *
     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
     * 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.
     */
@@ -168,7 +185,7 @@ public abstract class SystemService {
    }

    /**
     * @deprecated subclasses should extend {@link #onUnlockUser(int, int)} instead (which by
     * @deprecated subclasses should extend {@link #onUnlockUser(UserInfo)} instead (which by
     * default calls this method).
     */
    @Deprecated
@@ -185,6 +202,9 @@ public abstract class SystemService {
     * Code written inside system services should use
     * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
     * these states.
     * <p>
     * This method is only called when the service {@link #isSupported(UserInfo) supports} this
     * 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.
@@ -194,8 +214,8 @@ public abstract class SystemService {
    }

    /**
     * @deprecated subclasses should extend {@link #onSwitchUser(int, int)} instead (which by
     * default calls this method).
     * @deprecated subclasses should extend {@link #onSwitchUser(UserInfo, UserInfo)} instead
     * (which by default calls this method).
     */
    @Deprecated
    public void onSwitchUser(@UserIdInt int userHandle) {}
@@ -205,15 +225,21 @@ public abstract class SystemService {
     * special behavior for whichever user is currently in the foreground.  This is called
     * before any application processes are aware of the new user.
     *
     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} either
     * of the users ({@code from} or {@code to}).
     *
     * <b>NOTE: </b> both {@code from} and {@code to} are "live" objects
     * referenced by {@link UserManagerService} and hence should not be modified.
     *
     * @param from The information about the user being switched from.
     * @param to The information about the user being switched from to.
     */
    public void onSwitchUser(@NonNull UserInfo userInfo) {
        onSwitchUser(userInfo.id);
    public void onSwitchUser(@NonNull UserInfo from, @NonNull UserInfo to) {
        onSwitchUser(to.id);
    }

    /**
     * @deprecated subclasses should extend {@link #onStopUser(int, int)} instead (which by default
     * @deprecated subclasses should extend {@link #onStopUser(UserInfo)} instead (which by default
     * calls this method).
     */
    @Deprecated
@@ -225,6 +251,9 @@ public abstract class SystemService {
     * broadcast to the user; it is a good place to stop making use of any resources of that
     * user (such as binding to a service running in the user).
     *
     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
     * user.
     *
     * <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
     *
     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
@@ -235,7 +264,7 @@ public abstract class SystemService {
    }

    /**
     * @deprecated subclasses should extend {@link #onCleanupUser(int, int)} instead (which by
     * @deprecated subclasses should extend {@link #onCleanupUser(UserInfo)} instead (which by
     * default calls this method).
     */
    @Deprecated
@@ -246,8 +275,12 @@ public abstract class SystemService {
     * state they maintain for running users.  This is called after all application process
     * teardown of the user is complete.
     *
     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
     * user.
     *
     * <p>NOTE: When this callback is called, the CE storage for the target user may not be
     * accessible already.  Use {@link #onCleanupUser} instead if you need to access the CE storage.
     * accessible already.  Use {@link #onStopUser(UserInfo)} instead if you need to access the CE
     * storage.
     *
     * @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.
+68 −20
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.util.Slog;

@@ -41,8 +42,16 @@ import java.util.ArrayList;
 */
public class SystemServiceManager {
    private static final String TAG = "SystemServiceManager";
    private static final boolean DEBUG = false;
    private static final int SERVICE_CALL_WARN_TIME_MS = 50;

    // Constants used on onUser(...)
    private static final String START = "Start";
    private static final String UNLOCK = "Unlock";
    private static final String SWITCH = "Switch";
    private static final String STOP = "Stop";
    private static final String CLEANUP = "Cleanup";

    private static File sSystemDir;
    private final Context mContext;
    private boolean mSafeMode;
@@ -90,7 +99,6 @@ public class SystemServiceManager {
     * @return The service instance, never null.
     * @throws RuntimeException if the service fails to start.
     */
    @SuppressWarnings("unchecked")
    public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
@@ -212,64 +220,104 @@ public class SystemServiceManager {
     * Starts the given user.
     */
    public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
        onUser(t, "Start", userHandle, (s, u) -> s.onStartUser(u));
        onUser(t, START, userHandle);
    }

    /**
     * Unlocks the given user.
     */
    public void unlockUser(final @UserIdInt int userHandle) {
        onUser("Unlock", userHandle, (s, u) -> s.onUnlockUser(u));
        onUser(UNLOCK, userHandle);
    }

    /**
     * Switches to the given user.
     */
    public void switchUser(final @UserIdInt int userHandle) {
        onUser("Switch", userHandle, (s, u) -> s.onSwitchUser(u));
    public void switchUser(final @UserIdInt int from, final @UserIdInt int to) {
        onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from);
    }

    /**
     * Stops the given user.
     */
    public void stopUser(final @UserIdInt int userHandle) {
        onUser("Stop", userHandle, (s, u) -> s.onStopUser(u));
        onUser(STOP, userHandle);
    }

    /**
     * Cleans up the given user.
     */
    public void cleanupUser(final @UserIdInt int userHandle) {
        onUser("Cleanup", userHandle, (s, u) -> s.onCleanupUser(u));
        onUser(CLEANUP, userHandle);
    }

    private interface ServiceVisitor {
        void visit(@NonNull SystemService service, @NonNull UserInfo userInfo);
    private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) {
        onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle);
    }

    private void onUser(@NonNull String onWhat, @UserIdInt int userHandle,
            @NonNull ServiceVisitor visitor) {
        onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle, visitor);
    private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
            @UserIdInt int userHandle) {
        onUser(t, onWhat, userHandle, UserHandle.USER_NULL);
    }

    private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
            @UserIdInt int userHandle, @NonNull ServiceVisitor visitor) {
        t.traceBegin("ssm." + onWhat + "User-" + userHandle);
        Slog.i(TAG, "Calling on" + onWhat + "User u" + userHandle);
        final UserInfo userInfo = getUserInfo(userHandle);
            @UserIdInt int curUserId, @UserIdInt int prevUserId) {
        t.traceBegin("ssm." + onWhat + "User-" + curUserId);
        Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
        final UserInfo curUserInfo = getUserInfo(curUserId);
        final UserInfo prevUserInfo = prevUserId == UserHandle.USER_NULL ? null
                : getUserInfo(prevUserId);
        final int serviceLen = mServices.size();
        for (int i = 0; i < serviceLen; i++) {
            final SystemService service = mServices.get(i);
            final String serviceName = service.getClass().getName();
            t.traceBegin("ssm.on" + onWhat + "User-" + userHandle + " " + serviceName);
            boolean supported = service.isSupported(curUserInfo);

            // Must check if either curUser or prevUser is supported (for example, if switching from
            // unsupported to supported, we still need to notify the services)
            if (!supported && prevUserInfo != null) {
                supported = service.isSupported(prevUserInfo);
            }

            if (!supported) {
                if (DEBUG) {
                    Slog.d(TAG, "Skipping " + onWhat + "User-" + curUserId + " on service "
                            + serviceName + " because it's not supported (curUser: "
                            + curUserInfo + ", prevUser:" + prevUserInfo + ")");
                } else {
                    Slog.i(TAG,  "Skipping " + onWhat + "User-" + curUserId + " on "
                            + serviceName);
                }
                continue;
            }
            t.traceBegin("ssm.on" + onWhat + "User-" + curUserId + " " + serviceName);
            long time = SystemClock.elapsedRealtime();
            try {
                visitor.visit(service, userInfo);
                switch (onWhat) {
                    case SWITCH:
                        service.onSwitchUser(prevUserInfo, curUserInfo);
                        break;
                    case START:
                        service.onStartUser(curUserInfo);
                        break;
                    case UNLOCK:
                        service.onUnlockUser(curUserInfo);
                        break;
                    case STOP:
                        service.onStopUser(curUserInfo);
                        break;
                    case CLEANUP:
                        service.onCleanupUser(curUserInfo);
                        break;
                    default:
                        throw new IllegalArgumentException(onWhat + " what?");
                }
            } catch (Exception ex) {
                Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + userHandle
                Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUserInfo
                        + " to service " + serviceName, ex);
            }
            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + onWhat + "User ");
            warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
                    "on" + onWhat + "User-" + curUserId);
            t.traceEnd(); // what on service
        }
        t.traceEnd(); // main entry
+1 −1
Original line number Diff line number Diff line
@@ -2180,7 +2180,7 @@ class UserController implements Handler.Callback {
                        BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
                        Integer.toString(msg.arg1), msg.arg1);

                mInjector.getSystemServiceManager().switchUser(msg.arg1);
                mInjector.getSystemServiceManager().switchUser(msg.arg2, msg.arg1);
                break;
            case FOREGROUND_PROFILE_CHANGED_MSG:
                dispatchForegroundProfileChanged(msg.arg1);
+48 −32
Original line number Diff line number Diff line
@@ -52,8 +52,9 @@ import android.os.Parcel;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
@@ -95,14 +96,14 @@ import java.util.concurrent.Executor;
 */
public class VoiceInteractionManagerService extends SystemService {
    static final String TAG = "VoiceInteractionManagerService";
    static final boolean DEBUG = true; // TODO(b/133242016) STOPSHIP: change to false before R ships
    static final boolean DEBUG = false;

    final Context mContext;
    final ContentResolver mResolver;
    final DatabaseHelper mDbHelper;
    final ActivityManagerInternal mAmInternal;
    final ActivityTaskManagerInternal mAtmInternal;
    final UserManager mUserManager;
    final UserManagerInternal mUserManagerInternal;
    final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>();
    ShortcutServiceInternal mShortcutServiceInternal;
    SoundTriggerInternal mSoundTriggerInternal;
@@ -120,8 +121,8 @@ public class VoiceInteractionManagerService extends SystemService {
                LocalServices.getService(ActivityManagerInternal.class));
        mAtmInternal = Preconditions.checkNotNull(
                LocalServices.getService(ActivityTaskManagerInternal.class));
        mUserManager = Preconditions.checkNotNull(
                context.getSystemService(UserManager.class));
        mUserManagerInternal = Preconditions.checkNotNull(
                LocalServices.getService(UserManagerInternal.class));

        PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
                PermissionManagerServiceInternal.class);
@@ -156,38 +157,31 @@ public class VoiceInteractionManagerService extends SystemService {
        }
    }

    @Override
    public boolean isSupported(UserInfo userInfo) {
        return userInfo.isFull();
    }

    @Override
    public void onStartUser(@NonNull UserInfo userInfo) {
        if (DEBUG) Slog.d(TAG, "onStartUser(" + userInfo + ")");
        if (DEBUG_USER) 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(@NonNull UserInfo userInfo) {
        if (DEBUG) Slog.d(TAG, "onUnlockUser(" + userInfo + ")");
        if (DEBUG_USER) 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(@NonNull UserInfo userInfo) {
        if (DEBUG) Slog.d(TAG, "onSwitchUser(" + userInfo + ")");
    public void onSwitchUser(@NonNull UserInfo from, @NonNull UserInfo to) {
        if (DEBUG_USER) Slog.d(TAG, "onSwitchUser(" + from + " > " + to + ")");

        if (!userInfo.isFull()) {
            if (DEBUG) Slog.d(TAG,  "***** skipping on non-full user " + userInfo);
            return;
        }
        mServiceStub.switchUser(userInfo.id);
        mServiceStub.switchUser(to.id);
    }

    class LocalService extends VoiceInteractionManagerInternal {
@@ -225,6 +219,7 @@ public class VoiceInteractionManagerService extends SystemService {
        private boolean mSafeMode;
        private int mCurUser;
        private boolean mCurUserUnlocked;
        private boolean mCurUserSupported;
        private final boolean mEnableService;

        VoiceInteractionManagerServiceStub() {
@@ -292,9 +287,9 @@ public class VoiceInteractionManagerService extends SystemService {

        public void initForUser(int userHandle) {
            final TimingsTraceAndSlog t;
            if (DEBUG) {
                t = TimingsTraceAndSlog.newAsyncLog();
                t.traceBegin("VoiceInteractionSvc.initForUser(" + userHandle + ")");
            if (DEBUG_USER) {
                t = new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
                t.traceBegin("initForUser(" + userHandle + ")");
            } else {
                t = null;
            }
@@ -439,15 +434,21 @@ public class VoiceInteractionManagerService extends SystemService {
            new SettingsObserver(UiThread.getHandler());

            synchronized (this) {
                mCurUser = ActivityManager.getCurrentUser();
                setCurrentUserLocked(ActivityManager.getCurrentUser());
                switchImplementationIfNeededLocked(false);
            }
        }

        public void switchUser(int userHandle) {
        private void setCurrentUserLocked(@UserIdInt int userHandle) {
            mCurUser = userHandle;
            final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser);
            mCurUserSupported = isSupported(userInfo);
        }

        public void switchUser(@UserIdInt int userHandle) {
            FgThread.getHandler().post(() -> {
                synchronized (this) {
                    mCurUser = userHandle;
                    setCurrentUserLocked(userHandle);
                    mCurUserUnlocked = false;
                    switchImplementationIfNeededLocked(false);
                }
@@ -461,10 +462,22 @@ public class VoiceInteractionManagerService extends SystemService {
        }

        void switchImplementationIfNeededLocked(boolean force) {
            if (!mCurUserSupported) {
                if (DEBUG_USER) {
                    Slog.d(TAG, "switchImplementationIfNeeded(): skipping on unsuported user "
                            + mCurUser);
                }
                if (mImpl != null) {
                    mImpl.shutdownLocked();
                    setImplLocked(null);
                }
                return;
            }

            final TimingsTraceAndSlog t;
            if (DEBUG) {
                t = TimingsTraceAndSlog.newAsyncLog();
                t.traceBegin("VoiceInteractionSvc.switchImplementation(" + mCurUser + ")");
            if (DEBUG_USER) {
                t = new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
                t.traceBegin("switchImplementation(" + mCurUser + ")");
            } else {
                t = null;
            }
@@ -494,7 +507,7 @@ public class VoiceInteractionManagerService extends SystemService {

                final boolean hasComponent = serviceComponent != null && serviceInfo != null;

                if (mUserManager.isUserUnlockingOrUnlocked(mCurUser)) {
                if (mUserManagerInternal.isUserUnlockingOrUnlocked(mCurUser)) {
                    if (hasComponent) {
                        mShortcutServiceInternal.setShortcutHostPackage(TAG,
                                serviceComponent.getPackageName(), mCurUser);
@@ -1275,6 +1288,9 @@ public class VoiceInteractionManagerService extends SystemService {
            synchronized (this) {
                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)");
                pw.println("  mEnableService: " + mEnableService);
                pw.println("  mCurUser: " + mCurUser);
                pw.println("  mCurUserUnlocked: " + mCurUserUnlocked);
                pw.println("  mCurUserSupported: " + mCurUserSupported);
                if (mImpl == null) {
                    pw.println("  (No active implementation)");
                    return;