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