blob: 048bef7b19f5cbe1a4bc87638ba1c6311ce0101b [file] [log] [blame]
Ruben Brunk279d34a2017-03-30 18:01:33 -07001/*
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 com.android.server.LocalServices;
24import 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 */
44final 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 */
116 public VrController(final Object globalAmLock) {
117 mGlobalAmLock = globalAmLock;
118 }
119
120 /**
121 * Called when ActivityManagerService receives its systemReady call during boot.
122 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
410 private boolean hasPersistentVrFlagSet() {
411 return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
412 }
413
414 @Override
415 public String toString() {
416 return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
417 }
418}