1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.am; 18 19 import android.content.ComponentName; 20 import android.os.Process; 21 import android.service.vr.IPersistentVrStateCallbacks; 22 import android.util.Slog; 23 import com.android.server.LocalServices; 24 import com.android.server.vr.VrManagerInternal; 25 26 /** 27 * Helper class for {@link ActivityManagerService} responsible for VrMode-related ActivityManager 28 * functionality. 29 * 30 * <p>Specifically, this class is responsible for: 31 * <ul> 32 * <li>Adjusting the scheduling of VR render threads while in VR mode. 33 * <li>Handling ActivityManager calls to set a VR or a 'persistent' VR thread. 34 * <li>Tracking the state of ActivityManagerService's view of VR-related behavior flags. 35 * </ul> 36 * 37 * <p>This is NOT the class that manages the system VR mode lifecycle. The class responsible for 38 * handling everything related to VR mode state changes (e.g. the lifecycles of the associated 39 * VrListenerService, VrStateCallbacks, VR HAL etc.) is VrManagerService. 40 * 41 * <p>This class is exclusively for use by ActivityManagerService. Do not add callbacks or other 42 * functionality to this for things that belong in VrManagerService. 43 */ 44 final class VrController { 45 private static final String TAG = "VrController"; 46 47 // VR state flags. 48 private static final int FLAG_NON_VR_MODE = 0; 49 private static final int FLAG_VR_MODE = 1; 50 private static final int FLAG_PERSISTENT_VR_MODE = 2; 51 52 // Invariants maintained for mVrState 53 // 54 // Always true: 55 // - Only a single VR-related thread will have elevated scheduling priorities at a time 56 // across all threads in all processes (and for all possible running modes). 57 // 58 // Always true while FLAG_PERSISTENT_VR_MODE is set: 59 // - An application has set a flag to run in persistent VR mode the next time VR mode is 60 // entered. The device may or may not be in VR mode. 61 // - mVrState will contain FLAG_PERSISTENT_VR_MODE 62 // - An application may set a persistent VR thread that gains elevated scheduling 63 // priorities via a call to setPersistentVrThread. 64 // - Calls to set a regular (non-persistent) VR thread via setVrThread will fail, and 65 // thread that had previously elevated its scheduling priority in this way is returned 66 // to its normal scheduling priority. 67 // 68 // Always true while FLAG_VR_MODE is set: 69 // - The current top application is running in VR mode. 70 // - mVrState will contain FLAG_VR_MODE 71 // 72 // While FLAG_VR_MODE is set without FLAG_PERSISTENT_VR_MODE: 73 // - The current top application may set one of its threads to run at an elevated 74 // scheduling priority via a call to setVrThread. 75 // 76 // While FLAG_VR_MODE is set with FLAG_PERSISTENT_VR_MODE: 77 // - The current top application may NOT set one of its threads to run at an elevated 78 // scheduling priority via a call to setVrThread (instead, the persistent VR thread will 79 // be kept if an application has set one). 80 // 81 // While mVrState == FLAG_NON_VR_MODE: 82 // - Calls to setVrThread will fail. 83 // - Calls to setPersistentVrThread will fail. 84 // - No threads will have elevated scheduling priority for VR. 85 // 86 private int mVrState = FLAG_NON_VR_MODE; 87 88 // The single VR render thread on the device that is given elevated scheduling priority. 89 private int mVrRenderThreadTid = 0; 90 91 private final Object mGlobalAmLock; 92 93 private final IPersistentVrStateCallbacks mPersistentVrModeListener = 94 new IPersistentVrStateCallbacks.Stub() { 95 @Override 96 public void onPersistentVrStateChanged(boolean enabled) { 97 synchronized(mGlobalAmLock) { 98 // Note: This is the only place where mVrState should have its 99 // FLAG_PERSISTENT_VR_MODE setting changed. 100 if (enabled) { 101 setVrRenderThreadLocked(0, ProcessList.SCHED_GROUP_TOP_APP, true); 102 mVrState |= FLAG_PERSISTENT_VR_MODE; 103 } else { 104 setPersistentVrRenderThreadLocked(0, true); 105 mVrState &= ~FLAG_PERSISTENT_VR_MODE; 106 } 107 } 108 } 109 }; 110 111 /** 112 * Create new VrController instance. 113 * 114 * @param globalAmLock the global ActivityManagerService lock. 115 */ VrController(final Object globalAmLock)116 public VrController(final Object globalAmLock) { 117 mGlobalAmLock = globalAmLock; 118 } 119 120 /** 121 * Called when ActivityManagerService receives its systemReady call during boot. 122 */ onSystemReady()123 public void onSystemReady() { 124 VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class); 125 if (vrManagerInternal != null) { 126 vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); 127 } 128 } 129 130 /** 131 * Called when ActivityManagerService's TOP_APP process has changed. 132 * 133 * <p>Note: This must be called with the global ActivityManagerService lock held. 134 * 135 * @param proc is the ProcessRecord of the process that entered or left the TOP_APP scheduling 136 * group. 137 */ onTopProcChangedLocked(ProcessRecord proc)138 public void onTopProcChangedLocked(ProcessRecord proc) { 139 if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { 140 setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, true); 141 } else { 142 if (proc.vrThreadTid == mVrRenderThreadTid) { 143 clearVrRenderThreadLocked(true); 144 } 145 } 146 } 147 148 /** 149 * Called when ActivityManagerService is switching VR mode for the TOP_APP process. 150 * 151 * @param record the ActivityRecord of the activity changing the system VR mode. 152 * @return {@code true} if the VR state changed. 153 */ onVrModeChanged(ActivityRecord record)154 public boolean onVrModeChanged(ActivityRecord record) { 155 // This message means that the top focused activity enabled VR mode (or an activity 156 // that previously set this has become focused). 157 VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); 158 if (vrService == null) { 159 // VR mode isn't supported on this device. 160 return false; 161 } 162 boolean vrMode; 163 ComponentName requestedPackage; 164 ComponentName callingPackage; 165 int userId; 166 boolean changed = false; 167 synchronized (mGlobalAmLock) { 168 vrMode = record.requestedVrComponent != null; 169 requestedPackage = record.requestedVrComponent; 170 userId = record.userId; 171 callingPackage = record.info.getComponentName(); 172 173 // Tell the VrController that a VR mode change is requested. 174 changed = changeVrModeLocked(vrMode, record.app); 175 } 176 177 // Tell VrManager that a VR mode changed is requested, VrManager will handle 178 // notifying all non-AM dependencies if needed. 179 vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage); 180 return changed; 181 } 182 183 /** 184 * Called to set an application's VR thread. 185 * 186 * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set, 187 * or the scheduling group of the thread is not for the current top app. If this succeeds, any 188 * previous VR thread will be returned to a normal sheduling priority; if this fails, the 189 * scheduling for the previous thread will be unaffected. 190 * 191 * <p>Note: This must be called with the global ActivityManagerService lock and the 192 * mPidsSelfLocked object locks held. 193 * 194 * @param tid the tid of the thread to set, or 0 to unset the current thread. 195 * @param pid the pid of the process owning the thread to set. 196 * @param proc the ProcessRecord of the process owning the thread to set. 197 */ setVrThreadLocked(int tid, int pid, ProcessRecord proc)198 public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) { 199 if (hasPersistentVrFlagSet()) { 200 Slog.w(TAG, "VR thread cannot be set in persistent VR mode!"); 201 return; 202 } 203 if (proc == null) { 204 Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!"); 205 return; 206 } 207 if (tid != 0) { 208 enforceThreadInProcess(tid, pid); 209 } 210 if (!inVrMode()) { 211 Slog.w(TAG, "VR thread cannot be set when not in VR mode!"); 212 } else { 213 setVrRenderThreadLocked(tid, proc.curSchedGroup, false); 214 } 215 proc.vrThreadTid = (tid > 0) ? tid : 0; 216 } 217 218 /** 219 * Called to set an application's persistent VR thread. 220 * 221 * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds, 222 * any previous VR thread will be returned to a normal sheduling priority; if this fails, 223 * the scheduling for the previous thread will be unaffected. 224 * 225 * <p>Note: This must be called with the global ActivityManagerService lock and the 226 * mPidsSelfLocked object locks held. 227 * 228 * @param tid the tid of the thread to set, or 0 to unset the current thread. 229 * @param pid the pid of the process owning the thread to set. 230 * @param proc the ProcessRecord of the process owning the thread to set. 231 */ setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc)232 public void setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc) { 233 if (!hasPersistentVrFlagSet()) { 234 Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!"); 235 return; 236 } 237 if (proc == null) { 238 Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!"); 239 return; 240 } 241 if (tid != 0) { 242 enforceThreadInProcess(tid, pid); 243 } 244 setPersistentVrRenderThreadLocked(tid, false); 245 } 246 247 /** 248 * Return {@code true} when UI features incompatible with VR mode should be disabled. 249 * 250 * <p>Note: This must be called with the global ActivityManagerService lock held. 251 */ shouldDisableNonVrUiLocked()252 public boolean shouldDisableNonVrUiLocked() { 253 return mVrState != FLAG_NON_VR_MODE; 254 } 255 256 /** 257 * Called when to update this VrController instance's state when the system VR mode is being 258 * changed. 259 * 260 * <p>Note: This must be called with the global ActivityManagerService lock held. 261 * 262 * @param vrMode {@code true} if the system VR mode is being enabled. 263 * @param proc the ProcessRecord of the process enabling the system VR mode. 264 * 265 * @return {@code true} if our state changed. 266 */ changeVrModeLocked(boolean vrMode, ProcessRecord proc)267 private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) { 268 final int oldVrState = mVrState; 269 270 // This is the only place where mVrState should have its FLAG_VR_MODE setting 271 // changed. 272 if (vrMode) { 273 mVrState |= FLAG_VR_MODE; 274 } else { 275 mVrState &= ~FLAG_VR_MODE; 276 } 277 278 boolean changed = (oldVrState != mVrState); 279 280 if (changed) { 281 if (proc != null) { 282 if (proc.vrThreadTid > 0) { 283 setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false); 284 } 285 } else { 286 clearVrRenderThreadLocked(false); 287 } 288 } 289 return changed; 290 } 291 292 /** 293 * Set the given thread as the new VR thread, and give it special scheduling priority. 294 * 295 * <p>If the current thread is this thread, do nothing. If the current thread is different from 296 * the given thread, the current thread will be returned to a normal scheduling priority. 297 * 298 * @param newTid the tid of the thread to set, or 0 to unset the current thread. 299 * @param suppressLogs {@code true} if any error logging should be disabled. 300 * 301 * @return the tid of the thread configured to run at the scheduling priority for VR 302 * mode after this call completes (this may be the previous thread). 303 */ updateVrRenderThreadLocked(int newTid, boolean suppressLogs)304 private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) { 305 if (mVrRenderThreadTid == newTid) { 306 return mVrRenderThreadTid; 307 } 308 309 if (mVrRenderThreadTid > 0) { 310 ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs); 311 mVrRenderThreadTid = 0; 312 } 313 314 if (newTid > 0) { 315 mVrRenderThreadTid = newTid; 316 ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs); 317 } 318 return mVrRenderThreadTid; 319 } 320 321 /** 322 * Set special scheduling for the given application persistent VR thread, if allowed. 323 * 324 * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds, 325 * any previous VR thread will be returned to a normal sheduling priority; if this fails, 326 * the scheduling for the previous thread will be unaffected. 327 * 328 * @param newTid the tid of the thread to set, or 0 to unset the current thread. 329 * @param suppressLogs {@code true} if any error logging should be disabled. 330 * 331 * @return the tid of the thread configured to run at the scheduling priority for VR 332 * mode after this call completes (this may be the previous thread). 333 */ setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs)334 private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) { 335 if (!hasPersistentVrFlagSet()) { 336 if (!suppressLogs) { 337 Slog.w(TAG, "Failed to set persistent VR thread, " 338 + "system not in persistent VR mode."); 339 } 340 return mVrRenderThreadTid; 341 } 342 return updateVrRenderThreadLocked(newTid, suppressLogs); 343 } 344 345 /** 346 * Set special scheduling for the given application VR thread, if allowed. 347 * 348 * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set, 349 * or the scheduling group of the thread is not for the current top app. If this succeeds, any 350 * previous VR thread will be returned to a normal sheduling priority; if this fails, the 351 * scheduling for the previous thread will be unaffected. 352 * 353 * @param newTid the tid of the thread to set, or 0 to unset the current thread. 354 * @param schedGroup the current scheduling group of the thread to set. 355 * @param suppressLogs {@code true} if any error logging should be disabled. 356 * 357 * @return the tid of the thread configured to run at the scheduling priority for VR 358 * mode after this call completes (this may be the previous thread). 359 */ setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs)360 private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) { 361 boolean inVr = inVrMode(); 362 boolean inPersistentVr = hasPersistentVrFlagSet(); 363 if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) { 364 if (!suppressLogs) { 365 String reason = "caller is not the current top application."; 366 if (!inVr) { 367 reason = "system not in VR mode."; 368 } else if (inPersistentVr) { 369 reason = "system in persistent VR mode."; 370 } 371 Slog.w(TAG, "Failed to set VR thread, " + reason); 372 } 373 return mVrRenderThreadTid; 374 } 375 return updateVrRenderThreadLocked(newTid, suppressLogs); 376 } 377 378 /** 379 * Unset any special scheduling used for the current VR render thread, and return it to normal 380 * scheduling priority. 381 * 382 * @param suppressLogs {@code true} if any error logging should be disabled. 383 */ clearVrRenderThreadLocked(boolean suppressLogs)384 private void clearVrRenderThreadLocked(boolean suppressLogs) { 385 updateVrRenderThreadLocked(0, suppressLogs); 386 } 387 388 /** 389 * Check that the given tid is running in the process for the given pid, and throw an exception 390 * if not. 391 */ enforceThreadInProcess(int tid, int pid)392 private void enforceThreadInProcess(int tid, int pid) { 393 if (!Process.isThreadInProcess(pid, tid)) { 394 throw new IllegalArgumentException("VR thread does not belong to process"); 395 } 396 } 397 398 /** 399 * True when the system is in VR mode. 400 */ inVrMode()401 private boolean inVrMode() { 402 return (mVrState & FLAG_VR_MODE) != 0; 403 } 404 405 /** 406 * True when the persistent VR mode flag has been set. 407 * 408 * Note: Currently this does not necessarily mean that the system is in VR mode. 409 */ hasPersistentVrFlagSet()410 private boolean hasPersistentVrFlagSet() { 411 return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0; 412 } 413 414 @Override toString()415 public String toString() { 416 return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid); 417 } 418 } 419