• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.util.proto.ProtoOutputStream;
24 import android.util.proto.ProtoUtils;
25 
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 
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 
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      */
VrController(final Object globalAmLock)131     public VrController(final Object globalAmLock) {
132         mGlobalAmLock = globalAmLock;
133     }
134 
135     /**
136      * Called when ActivityManagerService receives its systemReady call during boot.
137      */
onSystemReady()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      */
onTopProcChangedLocked(ProcessRecord proc)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      */
onVrModeChanged(ActivityRecord record)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;
181         int processId = -1;
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);
191 
192             if (record.app != null) {
193                 processId = record.app.pid;
194             }
195         }
196 
197         // Tell VrManager that a VR mode changed is requested, VrManager will handle
198         // notifying all non-AM dependencies if needed.
199         vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage);
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      */
setVrThreadLocked(int tid, int pid, ProcessRecord proc)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      */
setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc)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      */
shouldDisableNonVrUiLocked()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      */
changeVrModeLocked(boolean vrMode, ProcessRecord proc)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      */
updateVrRenderThreadLocked(int newTid, boolean suppressLogs)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      */
setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs)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      */
setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs)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      */
clearVrRenderThreadLocked(boolean suppressLogs)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      */
enforceThreadInProcess(int tid, int pid)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      */
inVrMode()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      */
hasPersistentVrFlagSet()430     private boolean hasPersistentVrFlagSet() {
431         return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
432     }
433 
434     @Override
toString()435     public String toString() {
436       return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
437     }
438 
writeToProto(ProtoOutputStream proto, long fieldId)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     }
446 }
447