Ruben Brunk | 279d34a | 2017-03-30 18:01:33 -0700 | [diff] [blame] | 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; |
Yi Jin | 148d7f4 | 2017-11-28 14:23:56 -0800 | [diff] [blame] | 23 | import android.util.proto.ProtoOutputStream; |
| 24 | import android.util.proto.ProtoUtils; |
| 25 | |
Ruben Brunk | 279d34a | 2017-03-30 18:01:33 -0700 | [diff] [blame] | 26 | import com.android.server.LocalServices; |
| 27 | import 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 | */ |
| 47 | final 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 | |
Yi Jin | 148d7f4 | 2017-11-28 14:23:56 -0800 | [diff] [blame] | 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 | |
Ruben Brunk | 279d34a | 2017-03-30 18:01:33 -0700 | [diff] [blame] | 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; |
Albert Chaulk | 16d2397 | 2017-07-14 12:32:57 -0400 | [diff] [blame] | 181 | int processId = -1; |
Ruben Brunk | 279d34a | 2017-03-30 18:01:33 -0700 | [diff] [blame] | 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); |
Albert Chaulk | 16d2397 | 2017-07-14 12:32:57 -0400 | [diff] [blame] | 191 | |
| 192 | if (record.app != null) { |
| 193 | processId = record.app.pid; |
| 194 | } |
Ruben Brunk | 279d34a | 2017-03-30 18:01:33 -0700 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | // Tell VrManager that a VR mode changed is requested, VrManager will handle |
| 198 | // notifying all non-AM dependencies if needed. |
Albert Chaulk | 16d2397 | 2017-07-14 12:32:57 -0400 | [diff] [blame] | 199 | vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage); |
Ruben Brunk | 279d34a | 2017-03-30 18:01:33 -0700 | [diff] [blame] | 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 | } |
Yi Jin | 148d7f4 | 2017-11-28 14:23:56 -0800 | [diff] [blame] | 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 | } |
Ruben Brunk | 279d34a | 2017-03-30 18:01:33 -0700 | [diff] [blame] | 446 | } |