Merge "Refactor VR state management in ActivityManagerService." into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e002ce70e3
@@ -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);
|
||||
|
||||
418
services/core/java/com/android/server/am/VrController.java
Normal file
418
services/core/java/com/android/server/am/VrController.java
Normal file
@@ -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.
|
||||
*
|
||||
* <p>Specifically, this class is responsible for:
|
||||
* <ul>
|
||||
* <li>Adjusting the scheduling of VR render threads while in VR mode.
|
||||
* <li>Handling ActivityManager calls to set a VR or a 'persistent' VR thread.
|
||||
* <li>Tracking the state of ActivityManagerService's view of VR-related behavior flags.
|
||||
* </ul>
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user