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.wm;
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.am.ActivityManagerService;
28 import com.android.server.am.ProcessList;
29 import com.android.server.am.VrControllerProto;
30 import com.android.server.vr.VrManagerInternal;
31 
32 /**
33  * Helper class for {@link ActivityManagerService} responsible for VrMode-related ActivityManager
34  * functionality.
35  *
36  * <p>Specifically, this class is responsible for:
37  * <ul>
38  * <li>Adjusting the scheduling of VR render threads while in VR mode.
39  * <li>Handling ActivityManager calls to set a VR or a 'persistent' VR thread.
40  * <li>Tracking the state of ActivityManagerService's view of VR-related behavior flags.
41  * </ul>
42  *
43  * <p>This is NOT the class that manages the system VR mode lifecycle. The class responsible for
44  * handling everything related to VR mode state changes (e.g. the lifecycles of the associated
45  * VrListenerService, VrStateCallbacks, VR HAL etc.) is VrManagerService.
46  *
47  * <p>This class is exclusively for use by ActivityManagerService. Do not add callbacks or other
48  * functionality to this for things that belong in VrManagerService.
49  */
50 final class VrController {
51     private static final String TAG = "VrController";
52 
53     // VR state flags.
54     private static final int FLAG_NON_VR_MODE = 0;
55     private static final int FLAG_VR_MODE = 1;
56     private static final int FLAG_PERSISTENT_VR_MODE = 2;
57 
58     // Keep the enum lists in sync
59     private static int[] ORIG_ENUMS = new int[] {
60             FLAG_NON_VR_MODE,
61             FLAG_VR_MODE,
62             FLAG_PERSISTENT_VR_MODE,
63     };
64     private static int[] PROTO_ENUMS = new int[] {
65             VrControllerProto.FLAG_NON_VR_MODE,
66             VrControllerProto.FLAG_VR_MODE,
67             VrControllerProto.FLAG_PERSISTENT_VR_MODE,
68     };
69 
70     // Invariants maintained for mVrState
71     //
72     //   Always true:
73     //      - Only a single VR-related thread will have elevated scheduling priorities at a time
74     //        across all threads in all processes (and for all possible running modes).
75     //
76     //   Always true while FLAG_PERSISTENT_VR_MODE is set:
77     //      - An application has set a flag to run in persistent VR mode the next time VR mode is
78     //        entered. The device may or may not be in VR mode.
79     //      - mVrState will contain FLAG_PERSISTENT_VR_MODE
80     //      - An application may set a persistent VR thread that gains elevated scheduling
81     //        priorities via a call to setPersistentVrThread.
82     //      - Calls to set a regular (non-persistent) VR thread via setVrThread will fail, and
83     //        thread that had previously elevated its scheduling priority in this way is returned
84     //        to its normal scheduling priority.
85     //
86     //   Always true while FLAG_VR_MODE is set:
87     //      - The current top application is running in VR mode.
88     //      - mVrState will contain FLAG_VR_MODE
89     //
90     //   While FLAG_VR_MODE is set without FLAG_PERSISTENT_VR_MODE:
91     //      - The current top application may set one of its threads to run at an elevated
92     //        scheduling priority via a call to setVrThread.
93     //
94     //   While FLAG_VR_MODE is set with FLAG_PERSISTENT_VR_MODE:
95     //      - The current top application may NOT set one of its threads to run at an elevated
96     //        scheduling priority via a call to setVrThread (instead, the persistent VR thread will
97     //        be kept if an application has set one).
98     //
99     //   While mVrState == FLAG_NON_VR_MODE:
100     //      - Calls to setVrThread will fail.
101     //      - Calls to setPersistentVrThread will fail.
102     //      - No threads will have elevated scheduling priority for VR.
103     //
104     private volatile int mVrState = FLAG_NON_VR_MODE;
105 
106     // The single VR render thread on the device that is given elevated scheduling priority.
107     private int mVrRenderThreadTid = 0;
108 
109     private final Object mGlobalAmLock;
110 
111     private final IPersistentVrStateCallbacks mPersistentVrModeListener =
112             new IPersistentVrStateCallbacks.Stub() {
113         @Override
114         public void onPersistentVrStateChanged(boolean enabled) {
115             synchronized(mGlobalAmLock) {
116                 // Note: This is the only place where mVrState should have its
117                 // FLAG_PERSISTENT_VR_MODE setting changed.
118                 if (enabled) {
119                     setVrRenderThreadLocked(0, ProcessList.SCHED_GROUP_TOP_APP, true);
120                     mVrState |= FLAG_PERSISTENT_VR_MODE;
121                 } else {
122                     setPersistentVrRenderThreadLocked(0, true);
123                     mVrState &= ~FLAG_PERSISTENT_VR_MODE;
124                 }
125             }
126         }
127     };
128 
129     /** If it is null after system ready, then VR mode is not supported. */
130     VrManagerInternal mVrService;
131 
132     /**
133      * Create new VrController instance.
134      *
135      * @param globalAmLock the global ActivityManagerService lock.
136      */
VrController(final Object globalAmLock)137     public VrController(final Object globalAmLock) {
138         mGlobalAmLock = globalAmLock;
139     }
140 
141     /**
142      * Called when ActivityManagerService receives its systemReady call during boot.
143      */
onSystemReady()144     public void onSystemReady() {
145         VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class);
146         if (vrManagerInternal != null) {
147             mVrService = vrManagerInternal;
148             vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
149         }
150     }
151 
152     /**
153      * Called without lock to determine whether to call {@link #onTopProcChangedLocked} in lock. It
154      * is used to optimize performance for the path that may have lock contention frequently.
155      */
isInterestingToSchedGroup()156     boolean isInterestingToSchedGroup() {
157         return (mVrState & (FLAG_VR_MODE | FLAG_PERSISTENT_VR_MODE)) != 0;
158     }
159 
160     /**
161      * Called when ActivityManagerService's TOP_APP process has changed.
162      *
163      * <p>Note: This must be called with the global ActivityManagerService lock held.
164      *
165      * @param proc is the WindowProcessController of the process that entered or left the TOP_APP
166      *            scheduling group.
167      */
onTopProcChangedLocked(WindowProcessController proc)168     public void onTopProcChangedLocked(WindowProcessController proc) {
169         final int curSchedGroup = proc.getCurrentSchedulingGroup();
170         if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
171             setVrRenderThreadLocked(proc.mVrThreadTid, curSchedGroup, true);
172         } else {
173             if (proc.mVrThreadTid == mVrRenderThreadTid) {
174                 clearVrRenderThreadLocked(true);
175             }
176         }
177     }
178 
179     /**
180      * Called when ActivityManagerService is switching VR mode for the TOP_APP process.
181      *
182      * @param record the ActivityRecord of the activity changing the system VR mode.
183      * @return {@code true} if the VR state changed.
184      */
onVrModeChanged(ActivityRecord record)185     public boolean onVrModeChanged(ActivityRecord record) {
186         // This message means that the top focused activity enabled VR mode (or an activity
187         // that previously set this has become focused).
188         final VrManagerInternal vrService = mVrService;
189         if (vrService == null) {
190             // VR mode isn't supported on this device.
191             return false;
192         }
193         boolean vrMode;
194         ComponentName requestedPackage;
195         ComponentName callingPackage;
196         int userId;
197         int processId = -1;
198         boolean changed = false;
199         synchronized (mGlobalAmLock) {
200             vrMode = record.requestedVrComponent != null;
201             requestedPackage = record.requestedVrComponent;
202             userId = record.mUserId;
203             callingPackage = record.info.getComponentName();
204 
205             // Tell the VrController that a VR mode change is requested.
206             changed = changeVrModeLocked(vrMode, record.app);
207 
208             if (record.app != null) {
209                 processId = record.app.getPid();
210             }
211         }
212 
213         // Tell VrManager that a VR mode changed is requested, VrManager will handle
214         // notifying all non-AM dependencies if needed.
215         vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage);
216         return changed;
217     }
218 
219     /**
220      * Called to set an application's VR thread.
221      *
222      * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
223      * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
224      * previous VR thread will be returned to a normal sheduling priority; if this fails, the
225      * scheduling for the previous thread will be unaffected.
226      *
227      * <p>Note: This must be called with the global ActivityManagerService lock and the
228      *     mPidsSelfLocked object locks held.
229      *
230      * @param tid the tid of the thread to set, or 0 to unset the current thread.
231      * @param pid the pid of the process owning the thread to set.
232      * @param proc the WindowProcessController of the process owning the thread to set.
233      */
setVrThreadLocked(int tid, int pid, WindowProcessController proc)234     public void setVrThreadLocked(int tid, int pid, WindowProcessController proc) {
235         if (hasPersistentVrFlagSet()) {
236             Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
237             return;
238         }
239         if (proc == null) {
240            Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
241            return;
242         }
243         if (tid != 0) {
244             enforceThreadInProcess(tid, pid);
245         }
246         if (!inVrMode()) {
247             Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
248         } else {
249             setVrRenderThreadLocked(tid, proc.getCurrentSchedulingGroup(), false);
250         }
251         proc.mVrThreadTid = (tid > 0) ? tid : 0;
252     }
253 
254     /**
255      * Called to set an application's persistent VR thread.
256      *
257      * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
258      * any previous VR thread will be returned to a normal sheduling priority; if this fails,
259      * the scheduling for the previous thread will be unaffected.
260      *
261      * <p>Note: This must be called with the global ActivityManagerService lock and the
262      *     mPidsSelfLocked object locks held.
263      *
264      * @param tid the tid of the thread to set, or 0 to unset the current thread.
265      * @param pid the pid of the process owning the thread to set.
266      * @param proc the process owning the thread to set.
267      */
setPersistentVrThreadLocked(int tid, int pid, WindowProcessController proc)268     public void setPersistentVrThreadLocked(int tid, int pid, WindowProcessController proc) {
269         if (!hasPersistentVrFlagSet()) {
270             Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
271             return;
272         }
273         if (proc == null) {
274            Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
275            return;
276         }
277         if (tid != 0) {
278             enforceThreadInProcess(tid, pid);
279         }
280         setPersistentVrRenderThreadLocked(tid, false);
281     }
282 
283     /**
284      * Return {@code true} when UI features incompatible with VR mode should be disabled.
285      *
286      * <p>Note: This must be called with the global ActivityManagerService lock held.
287      */
shouldDisableNonVrUiLocked()288     public boolean shouldDisableNonVrUiLocked() {
289         return mVrState != FLAG_NON_VR_MODE;
290     }
291 
292     /**
293      * Called when to update this VrController instance's state when the system VR mode is being
294      * changed.
295      *
296      * <p>Note: This must be called with the global ActivityManagerService lock held.
297      *
298      * @param vrMode {@code true} if the system VR mode is being enabled.
299      * @param proc the WindowProcessController of the process enabling the system VR mode.
300      *
301      * @return {@code true} if our state changed.
302      */
changeVrModeLocked(boolean vrMode, WindowProcessController proc)303     private boolean changeVrModeLocked(boolean vrMode, WindowProcessController proc) {
304         final int oldVrState = mVrState;
305 
306         // This is the only place where mVrState should have its FLAG_VR_MODE setting
307         // changed.
308         if (vrMode) {
309             mVrState |= FLAG_VR_MODE;
310         } else {
311             mVrState &= ~FLAG_VR_MODE;
312         }
313 
314         boolean changed = (oldVrState != mVrState);
315 
316         if (changed) {
317             if (proc != null) {
318                 if (proc.mVrThreadTid > 0) {
319                     setVrRenderThreadLocked(
320                             proc.mVrThreadTid, proc.getCurrentSchedulingGroup(), false);
321                 }
322             } else {
323               clearVrRenderThreadLocked(false);
324             }
325         }
326         return changed;
327     }
328 
329     /**
330      * Set the given thread as the new VR thread, and give it special scheduling priority.
331      *
332      * <p>If the current thread is this thread, do nothing. If the current thread is different from
333      * the given thread, the current thread will be returned to a normal scheduling priority.
334      *
335      * @param newTid the tid of the thread to set, or 0 to unset the current thread.
336      * @param suppressLogs {@code true} if any error logging should be disabled.
337      *
338      * @return the tid of the thread configured to run at the scheduling priority for VR
339      *          mode after this call completes (this may be the previous thread).
340      */
updateVrRenderThreadLocked(int newTid, boolean suppressLogs)341     private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) {
342         if (mVrRenderThreadTid == newTid) {
343             return mVrRenderThreadTid;
344         }
345 
346         if (mVrRenderThreadTid > 0) {
347             ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs);
348             mVrRenderThreadTid = 0;
349         }
350 
351         if (newTid > 0) {
352             mVrRenderThreadTid = newTid;
353             ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs);
354         }
355         return mVrRenderThreadTid;
356     }
357 
358     /**
359      * Set special scheduling for the given application persistent VR thread, if allowed.
360      *
361      * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
362      * any previous VR thread will be returned to a normal sheduling priority; if this fails,
363      * the scheduling for the previous thread will be unaffected.
364      *
365      * @param newTid the tid of the thread to set, or 0 to unset the current thread.
366      * @param suppressLogs {@code true} if any error logging should be disabled.
367      *
368      * @return the tid of the thread configured to run at the scheduling priority for VR
369      *          mode after this call completes (this may be the previous thread).
370      */
setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs)371     private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) {
372        if (!hasPersistentVrFlagSet()) {
373             if (!suppressLogs) {
374                 Slog.w(TAG, "Failed to set persistent VR thread, "
375                         + "system not in persistent VR mode.");
376             }
377             return mVrRenderThreadTid;
378         }
379         return updateVrRenderThreadLocked(newTid, suppressLogs);
380     }
381 
382     /**
383      * Set special scheduling for the given application VR thread, if allowed.
384      *
385      * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
386      * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
387      * previous VR thread will be returned to a normal sheduling priority; if this fails, the
388      * scheduling for the previous thread will be unaffected.
389      *
390      * @param newTid the tid of the thread to set, or 0 to unset the current thread.
391      * @param schedGroup the current scheduling group of the thread to set.
392      * @param suppressLogs {@code true} if any error logging should be disabled.
393      *
394      * @return the tid of the thread configured to run at the scheduling priority for VR
395      *          mode after this call completes (this may be the previous thread).
396      */
setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs)397     private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) {
398         boolean inVr = inVrMode();
399         boolean inPersistentVr = hasPersistentVrFlagSet();
400         if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
401             if (!suppressLogs) {
402                String reason = "caller is not the current top application.";
403                if (!inVr) {
404                    reason = "system not in VR mode.";
405                } else if (inPersistentVr) {
406                    reason = "system in persistent VR mode.";
407                }
408                Slog.w(TAG, "Failed to set VR thread, " + reason);
409             }
410             return mVrRenderThreadTid;
411         }
412         return updateVrRenderThreadLocked(newTid, suppressLogs);
413     }
414 
415     /**
416      * Unset any special scheduling used for the current VR render thread, and return it to normal
417      * scheduling priority.
418      *
419      * @param suppressLogs {@code true} if any error logging should be disabled.
420      */
clearVrRenderThreadLocked(boolean suppressLogs)421     private void clearVrRenderThreadLocked(boolean suppressLogs) {
422         updateVrRenderThreadLocked(0, suppressLogs);
423     }
424 
425     /**
426      * Check that the given tid is running in the process for the given pid, and throw an exception
427      * if not.
428      */
enforceThreadInProcess(int tid, int pid)429     private void enforceThreadInProcess(int tid, int pid) {
430         if (!Process.isThreadInProcess(pid, tid)) {
431             throw new IllegalArgumentException("VR thread does not belong to process");
432         }
433     }
434 
435     /**
436      * True when the system is in VR mode.
437      */
inVrMode()438     private boolean inVrMode() {
439         return (mVrState & FLAG_VR_MODE) != 0;
440     }
441 
442     /**
443      * True when the persistent VR mode flag has been set.
444      *
445      * Note: Currently this does not necessarily mean that the system is in VR mode.
446      */
hasPersistentVrFlagSet()447     private boolean hasPersistentVrFlagSet() {
448         return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
449     }
450 
451     @Override
toString()452     public String toString() {
453       return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
454     }
455 
dumpDebug(ProtoOutputStream proto, long fieldId)456     void dumpDebug(ProtoOutputStream proto, long fieldId) {
457         final long token = proto.start(fieldId);
458         ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, VrControllerProto.VR_MODE,
459                 mVrState, ORIG_ENUMS, PROTO_ENUMS);
460         proto.write(VrControllerProto.RENDER_THREAD_ID, mVrRenderThreadTid);
461         proto.end(token);
462     }
463 }
464