diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ee2fdba715769..7ef38b278868a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -333,7 +333,6 @@ import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; import android.service.voice.VoiceInteractionSession; -import android.service.vr.IPersistentVrStateCallbacks; import android.telecom.TelecomManager; import android.text.TextUtils; import android.text.format.DateUtils; @@ -664,70 +663,8 @@ public class ActivityManagerService extends IActivityManager.Stub // default action automatically. Important for devices without direct input // devices. 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) { - 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; - } - } - } - }; + private final VrController mVrController; // VR Compatibility Display Id. int mVrCompatibilityDisplayId = INVALID_DISPLAY; @@ -2447,53 +2384,7 @@ public class ActivityManagerService extends IActivityManager.Stub idleUids(); } break; case VR_MODE_CHANGE_MSG: { - VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); - 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); + mVrController.onVrModeChanged((ActivityRecord) msg.obj); } break; case NOTIFY_VR_SLEEPING_MSG: { notifyVrManagerOfSleepState(msg.arg1 != 0); @@ -2770,6 +2661,7 @@ public class ActivityManagerService extends IActivityManager.Stub mTaskChangeNotificationController = null; mUiHandler = injector.getUiHandler(null); mUserController = null; + mVrController = null; } // 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); + mVrController = new VrController(this); + GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); @@ -13263,23 +13157,12 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setVrThread(int tid) { - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { - throw new UnsupportedOperationException("VR mode not supported on this device!"); - } - + enforceSystemHasVrFeature(); 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) { final int pid = Binder.getCallingPid(); - proc = mPidsSelfLocked.get(pid); - if (proc != null && mVrState == VR_MODE && tid >= 0) { - proc.vrThreadTid = updateVrThreadLocked(proc, proc.vrThreadTid, pid, tid); - mTopAppVrThreadTid = proc.vrThreadTid; - } + final ProcessRecord proc = mPidsSelfLocked.get(pid); + mVrController.setVrThreadLocked(tid, pid, proc); } } } @@ -13287,72 +13170,71 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setPersistentVrThread(int tid) { 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() + ", uid=" + Binder.getCallingUid() + " requires " + permission.RESTRICTED_VR_ACCESS; Slog.w(TAG, msg); throw new SecurityException(msg); } - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { - throw new UnsupportedOperationException("VR mode not supported on this device!"); - } - + enforceSystemHasVrFeature(); 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) { final int pid = Binder.getCallingPid(); - mPersistentVrThreadTid = - updateVrThreadLocked(null, mPersistentVrThreadTid, pid, tid); + final ProcessRecord proc = mPidsSelfLocked.get(pid); + mVrController.setPersistentVrThreadLocked(tid, pid, proc); } } } /** - * Used by setVrThread and setPersistentVrThread to update a thread's priority. When proc is - * non-null it must be in SCHED_GROUP_TOP_APP. When it is null, the tid is unconditionally - * rescheduled. + * Schedule the given thread a normal 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. */ - private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) { - // 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 { - setThreadScheduler(lastTid, SCHED_OTHER, 0); - } catch (IllegalArgumentException e) { - // Ignore this. Only occurs in race condition where previous VR thread - // was destroyed during this method call. - } - } - - // promote to FIFO now if the tid is non-zero + static boolean scheduleAsRegularPriority(int tid, boolean suppressLogs) { try { - if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) - && tid > 0) { - setThreadScheduler(tid, - SCHED_FIFO | SCHED_RESET_ON_FORK, 1); - } - return tid; + Process.setThreadScheduler(tid, Process.SCHED_OTHER, 0); + return true; } catch (IllegalArgumentException e) { - Slog.e(TAG, "Failed to set scheduling policy, thread does" - + " not exist:\n" + e); + if (!suppressLogs) { + Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e); + } + } + return false; + } + + /** + * 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 { + Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + return true; + } catch (IllegalArgumentException e) { + if (!suppressLogs) { + 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 @@ -13944,10 +13826,7 @@ public class ActivityManagerService extends IActivityManager.Stub mLocalDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); mAssistUtils = new AssistUtils(mContext); - VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class); - if (vrManagerInternal != null) { - vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); - } + mVrController.onSystemReady(); // Make sure we have the current profile info, since it is needed for security checks. mUserController.onSystemReady(); mRecentTasks.onSystemReadyLocked(); @@ -15629,6 +15508,7 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mVoiceWakeLock" + mVoiceWakeLock); } } + pw.println(" mVrController=" + mVrController); if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { if (dumpPackage == null || dumpPackage.equals(mDebugApp) @@ -20055,7 +19935,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController.getCurrentUserIdLocked()); // 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(); 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 * 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 && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH && config.navigation == Configuration.NAVIGATION_NONAV); int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK; final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR && !(modeType == Configuration.UI_MODE_TYPE_WATCH && "user".equals(Build.TYPE)) - && modeType != Configuration.UI_MODE_TYPE_TELEVISION); - return inputMethodExists && uiModeSupportsDialogs && !inVrMode; + && modeType != Configuration.UI_MODE_TYPE_TELEVISION + && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET); + return inputMethodExists && uiModeSupportsDialogs; } @Override @@ -21596,32 +21477,14 @@ public class ActivityManagerService extends IActivityManager.Stub if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { // do nothing if we already switched to RT if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { - // Switch VR thread for app to SCHED_FIFO - 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 - } - } + mVrController.onTopProcChangedLocked(app); if (mUseFifoUiScheduling) { // Switch UI pipeline for app to SCHED_FIFO - app.savedPriority = getThreadPriority(app.pid); - try { - setThreadScheduler(app.pid, - SCHED_FIFO | SCHED_RESET_ON_FORK, 1); - } catch (IllegalArgumentException e) { - // thread died, ignore - } + app.savedPriority = Process.getThreadPriority(app.pid); + scheduleAsFifoPriority(app.pid, /* suppressLogs */true); if (app.renderThreadTid != 0) { - try { - setThreadScheduler(app.renderThreadTid, - SCHED_FIFO | SCHED_RESET_ON_FORK, 1); - } catch (IllegalArgumentException e) { - // thread died, ignore - } + scheduleAsFifoPriority(app.renderThreadTid, + /* suppressLogs */true); if (DEBUG_OOM_ADJ) { Slog.d("UI_FIFO", "Set RenderThread (TID " + app.renderThreadTid + ") to FIFO"); @@ -21645,12 +21508,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { - // Reset VR thread to SCHED_OTHER - // Safe to do even if we're not in VR mode - if (app.vrThreadTid != 0) { - setThreadScheduler(app.vrThreadTid, SCHED_OTHER, 0); - mTopAppVrThreadTid = 0; - } + mVrController.onTopProcChangedLocked(app); if (mUseFifoUiScheduling) { // Reset UI pipeline to SCHED_OTHER setThreadScheduler(app.pid, SCHED_OTHER, 0); diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java new file mode 100644 index 0000000000000..048bef7b19f5c --- /dev/null +++ b/services/core/java/com/android/server/am/VrController.java @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import android.content.ComponentName; +import android.os.Process; +import android.service.vr.IPersistentVrStateCallbacks; +import android.util.Slog; +import com.android.server.LocalServices; +import com.android.server.vr.VrManagerInternal; + +/** + * Helper class for {@link ActivityManagerService} responsible for VrMode-related ActivityManager + * functionality. + * + *
Specifically, this class is responsible for: + *
This is NOT the class that manages the system VR mode lifecycle. The class responsible for + * handling everything related to VR mode state changes (e.g. the lifecycles of the associated + * VrListenerService, VrStateCallbacks, VR HAL etc.) is VrManagerService. + * + *
This class is exclusively for use by ActivityManagerService. Do not add callbacks or other + * functionality to this for things that belong in VrManagerService. + */ +final class VrController { + private static final String TAG = "VrController"; + + // VR state flags. + private static final int FLAG_NON_VR_MODE = 0; + private static final int FLAG_VR_MODE = 1; + private static final int FLAG_PERSISTENT_VR_MODE = 2; + + // Invariants maintained for mVrState + // + // Always true: + // - Only a single VR-related thread will have elevated scheduling priorities at a time + // across all threads in all processes (and for all possible running modes). + // + // Always true while FLAG_PERSISTENT_VR_MODE is set: + // - An application has set a flag to run in persistent VR mode the next time VR mode is + // entered. The device may or may not be in VR mode. + // - mVrState will contain FLAG_PERSISTENT_VR_MODE + // - An application may set a persistent VR thread that gains elevated scheduling + // priorities via a call to setPersistentVrThread. + // - Calls to set a regular (non-persistent) VR thread via setVrThread will fail, and + // thread that had previously elevated its scheduling priority in this way is returned + // to its normal scheduling priority. + // + // Always true while FLAG_VR_MODE is set: + // - The current top application is running in VR mode. + // - mVrState will contain FLAG_VR_MODE + // + // While FLAG_VR_MODE is set without FLAG_PERSISTENT_VR_MODE: + // - The current top application may set one of its threads to run at an elevated + // scheduling priority via a call to setVrThread. + // + // While FLAG_VR_MODE is set with FLAG_PERSISTENT_VR_MODE: + // - The current top application may NOT set one of its threads to run at an elevated + // scheduling priority via a call to setVrThread (instead, the persistent VR thread will + // be kept if an application has set one). + // + // While mVrState == FLAG_NON_VR_MODE: + // - Calls to setVrThread will fail. + // - Calls to setPersistentVrThread will fail. + // - No threads will have elevated scheduling priority for VR. + // + private int mVrState = FLAG_NON_VR_MODE; + + // The single VR render thread on the device that is given elevated scheduling priority. + private int mVrRenderThreadTid = 0; + + private final Object mGlobalAmLock; + + private final IPersistentVrStateCallbacks mPersistentVrModeListener = + new IPersistentVrStateCallbacks.Stub() { + @Override + public void onPersistentVrStateChanged(boolean enabled) { + synchronized(mGlobalAmLock) { + // Note: This is the only place where mVrState should have its + // FLAG_PERSISTENT_VR_MODE setting changed. + if (enabled) { + setVrRenderThreadLocked(0, ProcessList.SCHED_GROUP_TOP_APP, true); + mVrState |= FLAG_PERSISTENT_VR_MODE; + } else { + setPersistentVrRenderThreadLocked(0, true); + mVrState &= ~FLAG_PERSISTENT_VR_MODE; + } + } + } + }; + + /** + * Create new VrController instance. + * + * @param globalAmLock the global ActivityManagerService lock. + */ + public VrController(final Object globalAmLock) { + mGlobalAmLock = globalAmLock; + } + + /** + * Called when ActivityManagerService receives its systemReady call during boot. + */ + public void onSystemReady() { + VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class); + if (vrManagerInternal != null) { + vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); + } + } + + /** + * Called when ActivityManagerService's TOP_APP process has changed. + * + *
Note: This must be called with the global ActivityManagerService lock held. + * + * @param proc is the ProcessRecord of the process that entered or left the TOP_APP scheduling + * group. + */ + public void onTopProcChangedLocked(ProcessRecord proc) { + if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { + setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, true); + } else { + if (proc.vrThreadTid == mVrRenderThreadTid) { + clearVrRenderThreadLocked(true); + } + } + } + + /** + * Called when ActivityManagerService is switching VR mode for the TOP_APP process. + * + * @param record the ActivityRecord of the activity changing the system VR mode. + * @return {@code true} if the VR state changed. + */ + public boolean onVrModeChanged(ActivityRecord record) { + // This message means that the top focused activity enabled VR mode (or an activity + // that previously set this has become focused). + VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); + if (vrService == null) { + // VR mode isn't supported on this device. + return false; + } + boolean vrMode; + ComponentName requestedPackage; + ComponentName callingPackage; + int userId; + boolean changed = false; + synchronized (mGlobalAmLock) { + vrMode = record.requestedVrComponent != null; + requestedPackage = record.requestedVrComponent; + userId = record.userId; + callingPackage = record.info.getComponentName(); + + // Tell the VrController that a VR mode change is requested. + changed = changeVrModeLocked(vrMode, record.app); + } + + // Tell VrManager that a VR mode changed is requested, VrManager will handle + // notifying all non-AM dependencies if needed. + vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage); + return changed; + } + + /** + * Called to set an application's VR thread. + * + *
This will fail if the system is not in VR mode, the system has the persistent VR flag set, + * or the scheduling group of the thread is not for the current top app. If this succeeds, any + * previous VR thread will be returned to a normal sheduling priority; if this fails, the + * scheduling for the previous thread will be unaffected. + * + *
Note: This must be called with the global ActivityManagerService lock and the + * mPidsSelfLocked object locks held. + * + * @param tid the tid of the thread to set, or 0 to unset the current thread. + * @param pid the pid of the process owning the thread to set. + * @param proc the ProcessRecord of the process owning the thread to set. + */ + public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) { + if (hasPersistentVrFlagSet()) { + Slog.w(TAG, "VR thread cannot be set in persistent VR mode!"); + return; + } + if (proc == null) { + Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!"); + return; + } + if (tid != 0) { + enforceThreadInProcess(tid, pid); + } + if (!inVrMode()) { + Slog.w(TAG, "VR thread cannot be set when not in VR mode!"); + } else { + setVrRenderThreadLocked(tid, proc.curSchedGroup, false); + } + proc.vrThreadTid = (tid > 0) ? tid : 0; + } + + /** + * Called to set an application's persistent VR thread. + * + *
This will fail if the system does not have the persistent VR flag set. If this succeeds, + * any previous VR thread will be returned to a normal sheduling priority; if this fails, + * the scheduling for the previous thread will be unaffected. + * + *
Note: This must be called with the global ActivityManagerService lock and the + * mPidsSelfLocked object locks held. + * + * @param tid the tid of the thread to set, or 0 to unset the current thread. + * @param pid the pid of the process owning the thread to set. + * @param proc the ProcessRecord of the process owning the thread to set. + */ + public void setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc) { + if (!hasPersistentVrFlagSet()) { + Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!"); + return; + } + if (proc == null) { + Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!"); + return; + } + if (tid != 0) { + enforceThreadInProcess(tid, pid); + } + setPersistentVrRenderThreadLocked(tid, false); + } + + /** + * Return {@code true} when UI features incompatible with VR mode should be disabled. + * + *
Note: This must be called with the global ActivityManagerService lock held. + */ + public boolean shouldDisableNonVrUiLocked() { + return mVrState != FLAG_NON_VR_MODE; + } + + /** + * Called when to update this VrController instance's state when the system VR mode is being + * changed. + * + *
Note: This must be called with the global ActivityManagerService lock held. + * + * @param vrMode {@code true} if the system VR mode is being enabled. + * @param proc the ProcessRecord of the process enabling the system VR mode. + * + * @return {@code true} if our state changed. + */ + private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) { + final int oldVrState = mVrState; + + // This is the only place where mVrState should have its FLAG_VR_MODE setting + // changed. + if (vrMode) { + mVrState |= FLAG_VR_MODE; + } else { + mVrState &= ~FLAG_VR_MODE; + } + + boolean changed = (oldVrState != mVrState); + + if (changed) { + if (proc != null) { + if (proc.vrThreadTid > 0) { + setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false); + } + } else { + clearVrRenderThreadLocked(false); + } + } + return changed; + } + + /** + * Set the given thread as the new VR thread, and give it special scheduling priority. + * + *
If the current thread is this thread, do nothing. If the current thread is different from + * the given thread, the current thread will be returned to a normal scheduling priority. + * + * @param newTid the tid of the thread to set, or 0 to unset the current thread. + * @param suppressLogs {@code true} if any error logging should be disabled. + * + * @return the tid of the thread configured to run at the scheduling priority for VR + * mode after this call completes (this may be the previous thread). + */ + private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) { + if (mVrRenderThreadTid == newTid) { + return mVrRenderThreadTid; + } + + if (mVrRenderThreadTid > 0) { + ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs); + mVrRenderThreadTid = 0; + } + + if (newTid > 0) { + mVrRenderThreadTid = newTid; + ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs); + } + return mVrRenderThreadTid; + } + + /** + * Set special scheduling for the given application persistent VR thread, if allowed. + * + *
This will fail if the system does not have the persistent VR flag set. If this succeeds, + * any previous VR thread will be returned to a normal sheduling priority; if this fails, + * the scheduling for the previous thread will be unaffected. + * + * @param newTid the tid of the thread to set, or 0 to unset the current thread. + * @param suppressLogs {@code true} if any error logging should be disabled. + * + * @return the tid of the thread configured to run at the scheduling priority for VR + * mode after this call completes (this may be the previous thread). + */ + private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) { + if (!hasPersistentVrFlagSet()) { + if (!suppressLogs) { + Slog.w(TAG, "Failed to set persistent VR thread, " + + "system not in persistent VR mode."); + } + return mVrRenderThreadTid; + } + return updateVrRenderThreadLocked(newTid, suppressLogs); + } + + /** + * Set special scheduling for the given application VR thread, if allowed. + * + *
This will fail if the system is not in VR mode, the system has the persistent VR flag set, + * or the scheduling group of the thread is not for the current top app. If this succeeds, any + * previous VR thread will be returned to a normal sheduling priority; if this fails, the + * scheduling for the previous thread will be unaffected. + * + * @param newTid the tid of the thread to set, or 0 to unset the current thread. + * @param schedGroup the current scheduling group of the thread to set. + * @param suppressLogs {@code true} if any error logging should be disabled. + * + * @return the tid of the thread configured to run at the scheduling priority for VR + * mode after this call completes (this may be the previous thread). + */ + private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) { + boolean inVr = inVrMode(); + boolean inPersistentVr = hasPersistentVrFlagSet(); + if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) { + if (!suppressLogs) { + String reason = "caller is not the current top application."; + if (!inVr) { + reason = "system not in VR mode."; + } else if (inPersistentVr) { + reason = "system in persistent VR mode."; + } + Slog.w(TAG, "Failed to set VR thread, " + reason); + } + return mVrRenderThreadTid; + } + return updateVrRenderThreadLocked(newTid, suppressLogs); + } + + /** + * Unset any special scheduling used for the current VR render thread, and return it to normal + * scheduling priority. + * + * @param suppressLogs {@code true} if any error logging should be disabled. + */ + private void clearVrRenderThreadLocked(boolean suppressLogs) { + updateVrRenderThreadLocked(0, suppressLogs); + } + + /** + * Check that the given tid is running in the process for the given pid, and throw an exception + * if not. + */ + private void enforceThreadInProcess(int tid, int pid) { + if (!Process.isThreadInProcess(pid, tid)) { + throw new IllegalArgumentException("VR thread does not belong to process"); + } + } + + /** + * True when the system is in VR mode. + */ + private boolean inVrMode() { + return (mVrState & FLAG_VR_MODE) != 0; + } + + /** + * True when the persistent VR mode flag has been set. + * + * Note: Currently this does not necessarily mean that the system is in VR mode. + */ + private boolean hasPersistentVrFlagSet() { + return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0; + } + + @Override + public String toString() { + return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid); + } +}