blob: 45410d79afe78e4142fe9a5815f09b2e08525cf0 [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;
Yi Jin148d7f42017-11-28 14:23:56 -080023import android.util.proto.ProtoOutputStream;
24import android.util.proto.ProtoUtils;
25
Ruben Brunk279d34a2017-03-30 18:01:33 -070026import 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
Yi Jin148d7f42017-11-28 14:23:56 -080055 // 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 Brunk279d34a2017-03-30 18:01:33 -070067 // 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 Chaulk16d23972017-07-14 12:32:57 -0400181 int processId = -1;
Ruben Brunk279d34a2017-03-30 18:01:33 -0700182 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 Chaulk16d23972017-07-14 12:32:57 -0400191
192 if (record.app != null) {
193 processId = record.app.pid;
194 }
Ruben Brunk279d34a2017-03-30 18:01:33 -0700195 }
196
197 // Tell VrManager that a VR mode changed is requested, VrManager will handle
198 // notifying all non-AM dependencies if needed.
Albert Chaulk16d23972017-07-14 12:32:57 -0400199 vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage);
Ruben Brunk279d34a2017-03-30 18:01:33 -0700200 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 Jin148d7f42017-11-28 14:23:56 -0800438
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 Brunk279d34a2017-03-30 18:01:33 -0700446}