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