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

Commit 279d34af authored by Ruben Brunk's avatar Ruben Brunk Committed by Winson Chung
Browse files

Refactor VR state management in ActivityManagerService.

- Move VR-related state from ActivityManagerService into
  VrController.
- Move VR-related render thread scheduling logic from
  ActivityManagerService into VrController.
- Update state logic to better enforce invariants.

Bug: 36214284

Test: Covered by existing tests.
Change-Id: I02d1488da0c0f65a10dd0369f38f1a0c47cee174
parent 0ff1b458
Loading
Loading
Loading
Loading
+65 −207
Original line number Original line Diff line number Diff line
@@ -333,7 +333,6 @@ import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionSession;
import android.service.voice.VoiceInteractionSession;
import android.service.vr.IPersistentVrStateCallbacks;
import android.telecom.TelecomManager;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.DateUtils;
@@ -664,70 +663,8 @@ public class ActivityManagerService extends IActivityManager.Stub
    // default action automatically.  Important for devices without direct input
    // default action automatically.  Important for devices without direct input
    // devices.
    // devices.
    private boolean mShowDialogs = true;
    private boolean mShowDialogs = true;
    // VR state flags.
    static final int NON_VR_MODE = 0;
    static final int VR_MODE = 1;
    static final int PERSISTENT_VR_MODE = 2;
    private int mVrState = NON_VR_MODE;
    private int mTopAppVrThreadTid = 0;
    private int mPersistentVrThreadTid = 0;
    final IPersistentVrStateCallbacks mPersistentVrModeListener =
            new IPersistentVrStateCallbacks.Stub() {
        @Override
        public void onPersistentVrStateChanged(boolean enabled) {
            synchronized(ActivityManagerService.this) {
                // There are 4 possible cases here:
                //
                // Cases for enabled == true
                // Invariant: mVrState != PERSISTENT_VR_MODE;
                //    This is guaranteed as only this function sets mVrState to PERSISTENT_VR_MODE
                // Invariant: mPersistentVrThreadTid == 0
                //   This is guaranteed by VrManagerService, which only emits callbacks when the
                //   mode changes, and in setPersistentVrThread, which only sets
                //   mPersistentVrThreadTid when mVrState = PERSISTENT_VR_MODE
                // Case 1: mTopAppVrThreadTid > 0 (someone called setVrThread successfully and is
                // the top-app)
                //     We reset the app which currently has SCHED_FIFO (mPersistentVrThreadTid) to
                //     SCHED_OTHER
                // Case 2: mTopAppVrThreadTid == 0
                //     Do nothing
                //
                // Cases for enabled == false
                // Invariant: mVrState == PERSISTENT_VR_MODE;
                //     This is guaranteed by VrManagerService, which only emits callbacks when the
                //     mode changes, and the only other assignment of mVrState outside of this
                //     function checks if mVrState != PERSISTENT_VR_MODE
                // Invariant: mTopAppVrThreadTid == 0
                //     This is guaranteed in that mTopAppVrThreadTid is only set to a tid when
                //     mVrState is VR_MODE, and is explicitly set to 0 when setPersistentVrThread is
                //     called
                //   mPersistentVrThreadTid > 0 (someone called setPersistentVrThread successfully)
                //     3. Reset mPersistentVrThreadTidto SCHED_OTHER
                //   mPersistentVrThreadTid == 0
                //     4. Do nothing
                if (enabled) {
                    mVrState = PERSISTENT_VR_MODE;
                } else {
                    // Leaving persistent mode implies leaving VR mode.
                    mVrState = NON_VR_MODE;
                }
                if (mVrState == PERSISTENT_VR_MODE) {
    private final VrController mVrController;
                    if (mTopAppVrThreadTid > 0) {
                        // Ensure that when entering persistent VR mode the last top-app loses
                        // SCHED_FIFO.
                        setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0);
                        mTopAppVrThreadTid = 0;
                    }
                } else if (mPersistentVrThreadTid > 0) {
                    // Ensure that when leaving persistent VR mode we reschedule the high priority
                    // persistent thread.
                    setThreadScheduler(mPersistentVrThreadTid, SCHED_OTHER, 0);
                    mPersistentVrThreadTid = 0;
                }
            }
        }
    };
    // VR Compatibility Display Id.
    // VR Compatibility Display Id.
    int mVrCompatibilityDisplayId = INVALID_DISPLAY;
    int mVrCompatibilityDisplayId = INVALID_DISPLAY;
@@ -2447,53 +2384,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                idleUids();
                idleUids();
            } break;
            } break;
            case VR_MODE_CHANGE_MSG: {
            case VR_MODE_CHANGE_MSG: {
                VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
                mVrController.onVrModeChanged((ActivityRecord) msg.obj);
                if (vrService == null) {
                    break;
                }
                final ActivityRecord r = (ActivityRecord) msg.obj;
                boolean vrMode;
                boolean inVrMode;
                ComponentName requestedPackage;
                ComponentName callingPackage;
                int userId;
                synchronized (ActivityManagerService.this) {
                    vrMode = r.requestedVrComponent != null;
                    inVrMode = mVrState != NON_VR_MODE;
                    requestedPackage = r.requestedVrComponent;
                    userId = r.userId;
                    callingPackage = r.info.getComponentName();
                    if (vrMode != inVrMode) {
                        // Don't change state if we're in persistent VR mode, but do update thread
                        // priorities if necessary.
                        if (mVrState != PERSISTENT_VR_MODE) {
                            mVrState = vrMode ? VR_MODE : NON_VR_MODE;
                        }
                        mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), vrMode);
                        if (r.app != null) {
                            ProcessRecord proc = r.app;
                            if (proc.vrThreadTid > 0) {
                                if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                                    try {
                                        if (mVrState == VR_MODE) {
                                            setThreadScheduler(proc.vrThreadTid,
                                                SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
                                            mTopAppVrThreadTid = proc.vrThreadTid;
                                        } else {
                                            setThreadScheduler(proc.vrThreadTid,
                                                SCHED_OTHER, 0);
                                            mTopAppVrThreadTid = 0;
                                        }
                                    } catch (IllegalArgumentException e) {
                                        Slog.w(TAG, "Failed to set scheduling policy, thread does"
                                                + " not exist:\n" + e);
                                    }
                                }
                            }
                        }
                    }
                }
                vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
            } break;
            } break;
            case NOTIFY_VR_SLEEPING_MSG: {
            case NOTIFY_VR_SLEEPING_MSG: {
                notifyVrManagerOfSleepState(msg.arg1 != 0);
                notifyVrManagerOfSleepState(msg.arg1 != 0);
@@ -2770,6 +2661,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        mTaskChangeNotificationController = null;
        mTaskChangeNotificationController = null;
        mUiHandler = injector.getUiHandler(null);
        mUiHandler = injector.getUiHandler(null);
        mUserController = null;
        mUserController = null;
        mVrController = null;
    }
    }
    // Note: This method is invoked on the main thread but may need to attach various
    // Note: This method is invoked on the main thread but may need to attach various
@@ -2856,6 +2748,8 @@ public class ActivityManagerService extends IActivityManager.Stub
        mUserController = new UserController(this);
        mUserController = new UserController(this);
        mVrController = new VrController(this);
        GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
        GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
            ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
            ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -13263,23 +13157,12 @@ public class ActivityManagerService extends IActivityManager.Stub
    @Override
    @Override
    public void setVrThread(int tid) {
    public void setVrThread(int tid) {
        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
        enforceSystemHasVrFeature();
            throw new UnsupportedOperationException("VR mode not supported on this device!");
        }
        synchronized (this) {
        synchronized (this) {
            if (tid > 0 && mVrState == PERSISTENT_VR_MODE) {
                Slog.e(TAG, "VR thread cannot be set in persistent VR mode!");
                return;
            }
            ProcessRecord proc;
            synchronized (mPidsSelfLocked) {
            synchronized (mPidsSelfLocked) {
                final int pid = Binder.getCallingPid();
                final int pid = Binder.getCallingPid();
                proc = mPidsSelfLocked.get(pid);
                final ProcessRecord proc = mPidsSelfLocked.get(pid);
                if (proc != null && mVrState == VR_MODE && tid >= 0) {
                mVrController.setVrThreadLocked(tid, pid, proc);
                    proc.vrThreadTid = updateVrThreadLocked(proc, proc.vrThreadTid, pid, tid);
                    mTopAppVrThreadTid = proc.vrThreadTid;
                }
            }
            }
        }
        }
    }
    }
@@ -13287,72 +13170,71 @@ public class ActivityManagerService extends IActivityManager.Stub
    @Override
    @Override
    public void setPersistentVrThread(int tid) {
    public void setPersistentVrThread(int tid) {
        if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
        if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
            String msg = "Permission Denial: setPersistentVrThread() from pid="
            final String msg = "Permission Denial: setPersistentVrThread() from pid="
                    + Binder.getCallingPid()
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + ", uid=" + Binder.getCallingUid()
                    + " requires " + permission.RESTRICTED_VR_ACCESS;
                    + " requires " + permission.RESTRICTED_VR_ACCESS;
            Slog.w(TAG, msg);
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
            throw new SecurityException(msg);
        }
        }
        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
        enforceSystemHasVrFeature();
            throw new UnsupportedOperationException("VR mode not supported on this device!");
        }
        synchronized (this) {
        synchronized (this) {
            // Disable any existing VR thread.
            if (mTopAppVrThreadTid > 0) {
                setThreadScheduler(mTopAppVrThreadTid, SCHED_OTHER, 0);
                mTopAppVrThreadTid = 0;
            }
            if (tid > 0 && mVrState != PERSISTENT_VR_MODE) {
                Slog.e(TAG, "Persistent VR thread may only be set in persistent VR mode!");
                return;
            }
            ProcessRecord proc;
            synchronized (mPidsSelfLocked) {
            synchronized (mPidsSelfLocked) {
                final int pid = Binder.getCallingPid();
                final int pid = Binder.getCallingPid();
                mPersistentVrThreadTid =
                final ProcessRecord proc = mPidsSelfLocked.get(pid);
                        updateVrThreadLocked(null, mPersistentVrThreadTid, pid, tid);
                mVrController.setPersistentVrThreadLocked(tid, pid, proc);
            }
            }
        }
        }
    }
    }
    /**
    /**
     * Used by setVrThread and setPersistentVrThread to update a thread's priority. When proc is
     * Schedule the given thread a normal scheduling priority.
     * non-null it must be in SCHED_GROUP_TOP_APP.  When it is null, the tid is unconditionally
     *
     * rescheduled.
     * @param newTid the tid of the thread to adjust the scheduling of.
     * @param suppressLogs {@code true} if any error logging should be disabled.
     *
     * @return {@code true} if this succeeded.
     */
     */
    private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) {
    static boolean scheduleAsRegularPriority(int tid, boolean suppressLogs) {
        // ensure the tid belongs to the process
        if (!isThreadInProcess(pid, tid)) {
            throw new IllegalArgumentException("VR thread does not belong to process");
        }
        // reset existing VR thread to CFS if this thread still exists and belongs to
        // the calling process
        if (lastTid != 0 && isThreadInProcess(pid, lastTid)) {
        try {
        try {
                setThreadScheduler(lastTid, SCHED_OTHER, 0);
            Process.setThreadScheduler(tid, Process.SCHED_OTHER, 0);
            return true;
        } catch (IllegalArgumentException e) {
        } catch (IllegalArgumentException e) {
                // Ignore this.  Only occurs in race condition where previous VR thread
            if (!suppressLogs) {
                // was destroyed during this method call.
                Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
            }
            }
        }
        }
        return false;
    }
        // promote to FIFO now if the tid is non-zero
    /**
     * Schedule the given thread an FIFO scheduling priority.
     *
     * @param newTid the tid of the thread to adjust the scheduling of.
     * @param suppressLogs {@code true} if any error logging should be disabled.
     *
     * @return {@code true} if this succeeded.
     */
    static boolean scheduleAsFifoPriority(int tid, boolean suppressLogs) {
        try {
        try {
            if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP)
            Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
                    && tid > 0) {
            return true;
                setThreadScheduler(tid,
                    SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
            }
            return tid;
        } catch (IllegalArgumentException e) {
        } catch (IllegalArgumentException e) {
            Slog.e(TAG, "Failed to set scheduling policy, thread does"
            if (!suppressLogs) {
                   + " not exist:\n" + e);
                Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
            }
        }
        return false;
    }
    /**
     * Check that we have the features required for VR-related API calls, and throw an exception if
     * not.
     */
    private void enforceSystemHasVrFeature() {
        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
            throw new UnsupportedOperationException("VR mode not supported on this device!");
        }
        }
        return lastTid;
    }
    }
    @Override
    @Override
@@ -13944,10 +13826,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            mLocalDeviceIdleController
            mLocalDeviceIdleController
                    = LocalServices.getService(DeviceIdleController.LocalService.class);
                    = LocalServices.getService(DeviceIdleController.LocalService.class);
            mAssistUtils = new AssistUtils(mContext);
            mAssistUtils = new AssistUtils(mContext);
            VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
            mVrController.onSystemReady();
            if (vrManagerInternal != null) {
                vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
            }
            // Make sure we have the current profile info, since it is needed for security checks.
            // Make sure we have the current profile info, since it is needed for security checks.
            mUserController.onSystemReady();
            mUserController.onSystemReady();
            mRecentTasks.onSystemReadyLocked();
            mRecentTasks.onSystemReadyLocked();
@@ -15629,6 +15508,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
                pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
            }
            }
        }
        }
        pw.println("  mVrController=" + mVrController);
        if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
        if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                || mOrigWaitForDebugger) {
                || mOrigWaitForDebugger) {
            if (dumpPackage == null || dumpPackage.equals(mDebugApp)
            if (dumpPackage == null || dumpPackage.equals(mDebugApp)
@@ -20055,7 +19935,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                mUserController.getCurrentUserIdLocked());
                mUserController.getCurrentUserIdLocked());
        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
        // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
        mShowDialogs = shouldShowDialogs(mTempConfig, mVrState != NON_VR_MODE);
        mShowDialogs = shouldShowDialogs(mTempConfig);
        AttributeCache ac = AttributeCache.instance();
        AttributeCache ac = AttributeCache.instance();
        if (ac != null) {
        if (ac != null) {
@@ -20285,15 +20165,16 @@ public class ActivityManagerService extends IActivityManager.Stub
     * A thought: SystemUI might also want to get told about this, the Power
     * A thought: SystemUI might also want to get told about this, the Power
     * dialog / global actions also might want different behaviors.
     * dialog / global actions also might want different behaviors.
     */
     */
    private static boolean shouldShowDialogs(Configuration config, boolean inVrMode) {
    private static boolean shouldShowDialogs(Configuration config) {
        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
        final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
                                   && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
                                   && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
                                   && config.navigation == Configuration.NAVIGATION_NONAV);
                                   && config.navigation == Configuration.NAVIGATION_NONAV);
        int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
        int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK;
        final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
        final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR
                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE))
                && !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE))
                && modeType != Configuration.UI_MODE_TYPE_TELEVISION);
                && modeType != Configuration.UI_MODE_TYPE_TELEVISION
        return inputMethodExists && uiModeSupportsDialogs && !inVrMode;
                && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
        return inputMethodExists && uiModeSupportsDialogs;
    }
    }
    @Override
    @Override
@@ -21596,32 +21477,14 @@ public class ActivityManagerService extends IActivityManager.Stub
                    if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                    if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
                        // do nothing if we already switched to RT
                        // do nothing if we already switched to RT
                        if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                        if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                            // Switch VR thread for app to SCHED_FIFO
                            mVrController.onTopProcChangedLocked(app);
                            if (mVrState == VR_MODE && app.vrThreadTid != 0) {
                                try {
                                    setThreadScheduler(app.vrThreadTid,
                                        SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
                                    mTopAppVrThreadTid = app.vrThreadTid;
                                } catch (IllegalArgumentException e) {
                                    // thread died, ignore
                                }
                            }
                            if (mUseFifoUiScheduling) {
                            if (mUseFifoUiScheduling) {
                                // Switch UI pipeline for app to SCHED_FIFO
                                // Switch UI pipeline for app to SCHED_FIFO
                                app.savedPriority = getThreadPriority(app.pid);
                                app.savedPriority = Process.getThreadPriority(app.pid);
                                try {
                                scheduleAsFifoPriority(app.pid, /* suppressLogs */true);
                                    setThreadScheduler(app.pid,
                                        SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
                                } catch (IllegalArgumentException e) {
                                    // thread died, ignore
                                }
                                if (app.renderThreadTid != 0) {
                                if (app.renderThreadTid != 0) {
                                    try {
                                    scheduleAsFifoPriority(app.renderThreadTid,
                                        setThreadScheduler(app.renderThreadTid,
                                        /* suppressLogs */true);
                                            SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
                                    } catch (IllegalArgumentException e) {
                                        // thread died, ignore
                                    }
                                    if (DEBUG_OOM_ADJ) {
                                    if (DEBUG_OOM_ADJ) {
                                        Slog.d("UI_FIFO", "Set RenderThread (TID " +
                                        Slog.d("UI_FIFO", "Set RenderThread (TID " +
                                            app.renderThreadTid + ") to FIFO");
                                            app.renderThreadTid + ") to FIFO");
@@ -21645,12 +21508,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                        }
                        }
                    } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
                    } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
                               app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                               app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
                        // Reset VR thread to SCHED_OTHER
                        mVrController.onTopProcChangedLocked(app);
                        // Safe to do even if we're not in VR mode
                        if (app.vrThreadTid != 0) {
                            setThreadScheduler(app.vrThreadTid, SCHED_OTHER, 0);
                            mTopAppVrThreadTid = 0;
                        }
                        if (mUseFifoUiScheduling) {
                        if (mUseFifoUiScheduling) {
                            // Reset UI pipeline to SCHED_OTHER
                            // Reset UI pipeline to SCHED_OTHER
                            setThreadScheduler(app.pid, SCHED_OTHER, 0);
                            setThreadScheduler(app.pid, SCHED_OTHER, 0);
+418 −0

File added.

Preview size limit exceeded, changes collapsed.