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;
23 import com.android.server.LocalServices;
24 import 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  */
44 final 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      */
VrController(final Object globalAmLock)116     public VrController(final Object globalAmLock) {
117         mGlobalAmLock = globalAmLock;
118     }
119 
120     /**
121      * Called when ActivityManagerService receives its systemReady call during boot.
122      */
onSystemReady()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      */
onTopProcChangedLocked(ProcessRecord proc)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      */
onVrModeChanged(ActivityRecord record)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      */
setVrThreadLocked(int tid, int pid, ProcessRecord proc)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      */
setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc)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      */
shouldDisableNonVrUiLocked()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      */
changeVrModeLocked(boolean vrMode, ProcessRecord proc)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      */
updateVrRenderThreadLocked(int newTid, boolean suppressLogs)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      */
setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs)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      */
setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs)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      */
clearVrRenderThreadLocked(boolean suppressLogs)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      */
enforceThreadInProcess(int tid, int pid)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      */
inVrMode()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      */
hasPersistentVrFlagSet()410     private boolean hasPersistentVrFlagSet() {
411         return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
412     }
413 
414     @Override
toString()415     public String toString() {
416       return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
417     }
418 }
419