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
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        int processId = -1;
167        boolean changed = false;
168        synchronized (mGlobalAmLock) {
169            vrMode = record.requestedVrComponent != null;
170            requestedPackage = record.requestedVrComponent;
171            userId = record.userId;
172            callingPackage = record.info.getComponentName();
173
174            // Tell the VrController that a VR mode change is requested.
175            changed = changeVrModeLocked(vrMode, record.app);
176
177            if (record.app != null) {
178                processId = record.app.pid;
179            }
180        }
181
182        // Tell VrManager that a VR mode changed is requested, VrManager will handle
183        // notifying all non-AM dependencies if needed.
184        vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage);
185        return changed;
186    }
187
188    /**
189     * Called to set an application's VR thread.
190     *
191     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
192     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
193     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
194     * scheduling for the previous thread will be unaffected.
195     *
196     * <p>Note: This must be called with the global ActivityManagerService lock and the
197     *     mPidsSelfLocked object locks held.
198     *
199     * @param tid the tid of the thread to set, or 0 to unset the current thread.
200     * @param pid the pid of the process owning the thread to set.
201     * @param proc the ProcessRecord of the process owning the thread to set.
202     */
203    public void setVrThreadLocked(int tid, int pid, ProcessRecord proc) {
204        if (hasPersistentVrFlagSet()) {
205            Slog.w(TAG, "VR thread cannot be set in persistent VR mode!");
206            return;
207        }
208        if (proc == null) {
209           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
210           return;
211        }
212        if (tid != 0) {
213            enforceThreadInProcess(tid, pid);
214        }
215        if (!inVrMode()) {
216            Slog.w(TAG, "VR thread cannot be set when not in VR mode!");
217        } else {
218            setVrRenderThreadLocked(tid, proc.curSchedGroup, false);
219        }
220        proc.vrThreadTid = (tid > 0) ? tid : 0;
221    }
222
223    /**
224     * Called to set an application's persistent VR thread.
225     *
226     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
227     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
228     * the scheduling for the previous thread will be unaffected.
229     *
230     * <p>Note: This must be called with the global ActivityManagerService lock and the
231     *     mPidsSelfLocked object locks held.
232     *
233     * @param tid the tid of the thread to set, or 0 to unset the current thread.
234     * @param pid the pid of the process owning the thread to set.
235     * @param proc the ProcessRecord of the process owning the thread to set.
236     */
237    public void setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc) {
238        if (!hasPersistentVrFlagSet()) {
239            Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
240            return;
241        }
242        if (proc == null) {
243           Slog.w(TAG, "Persistent VR thread not set, calling process doesn't exist!");
244           return;
245        }
246        if (tid != 0) {
247            enforceThreadInProcess(tid, pid);
248        }
249        setPersistentVrRenderThreadLocked(tid, false);
250    }
251
252    /**
253     * Return {@code true} when UI features incompatible with VR mode should be disabled.
254     *
255     * <p>Note: This must be called with the global ActivityManagerService lock held.
256     */
257    public boolean shouldDisableNonVrUiLocked() {
258        return mVrState != FLAG_NON_VR_MODE;
259    }
260
261    /**
262     * Called when to update this VrController instance's state when the system VR mode is being
263     * changed.
264     *
265     * <p>Note: This must be called with the global ActivityManagerService lock held.
266     *
267     * @param vrMode {@code true} if the system VR mode is being enabled.
268     * @param proc the ProcessRecord of the process enabling the system VR mode.
269     *
270     * @return {@code true} if our state changed.
271     */
272    private boolean changeVrModeLocked(boolean vrMode, ProcessRecord proc) {
273        final int oldVrState = mVrState;
274
275        // This is the only place where mVrState should have its FLAG_VR_MODE setting
276        // changed.
277        if (vrMode) {
278            mVrState |= FLAG_VR_MODE;
279        } else {
280            mVrState &= ~FLAG_VR_MODE;
281        }
282
283        boolean changed = (oldVrState != mVrState);
284
285        if (changed) {
286            if (proc != null) {
287                if (proc.vrThreadTid > 0) {
288                    setVrRenderThreadLocked(proc.vrThreadTid, proc.curSchedGroup, false);
289                }
290            } else {
291              clearVrRenderThreadLocked(false);
292            }
293        }
294        return changed;
295    }
296
297    /**
298     * Set the given thread as the new VR thread, and give it special scheduling priority.
299     *
300     * <p>If the current thread is this thread, do nothing. If the current thread is different from
301     * the given thread, the current thread will be returned to a normal scheduling priority.
302     *
303     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
304     * @param suppressLogs {@code true} if any error logging should be disabled.
305     *
306     * @return the tid of the thread configured to run at the scheduling priority for VR
307     *          mode after this call completes (this may be the previous thread).
308     */
309    private int updateVrRenderThreadLocked(int newTid, boolean suppressLogs) {
310        if (mVrRenderThreadTid == newTid) {
311            return mVrRenderThreadTid;
312        }
313
314        if (mVrRenderThreadTid > 0) {
315            ActivityManagerService.scheduleAsRegularPriority(mVrRenderThreadTid, suppressLogs);
316            mVrRenderThreadTid = 0;
317        }
318
319        if (newTid > 0) {
320            mVrRenderThreadTid = newTid;
321            ActivityManagerService.scheduleAsFifoPriority(mVrRenderThreadTid, suppressLogs);
322        }
323        return mVrRenderThreadTid;
324    }
325
326    /**
327     * Set special scheduling for the given application persistent VR thread, if allowed.
328     *
329     * <p>This will fail if the system does not have the persistent VR flag set. If this succeeds,
330     * any previous VR thread will be returned to a normal sheduling priority; if this fails,
331     * the scheduling for the previous thread will be unaffected.
332     *
333     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
334     * @param suppressLogs {@code true} if any error logging should be disabled.
335     *
336     * @return the tid of the thread configured to run at the scheduling priority for VR
337     *          mode after this call completes (this may be the previous thread).
338     */
339    private int setPersistentVrRenderThreadLocked(int newTid, boolean suppressLogs) {
340       if (!hasPersistentVrFlagSet()) {
341            if (!suppressLogs) {
342                Slog.w(TAG, "Failed to set persistent VR thread, "
343                        + "system not in persistent VR mode.");
344            }
345            return mVrRenderThreadTid;
346        }
347        return updateVrRenderThreadLocked(newTid, suppressLogs);
348    }
349
350    /**
351     * Set special scheduling for the given application VR thread, if allowed.
352     *
353     * <p>This will fail if the system is not in VR mode, the system has the persistent VR flag set,
354     * or the scheduling group of the thread is not for the current top app.  If this succeeds, any
355     * previous VR thread will be returned to a normal sheduling priority; if this fails, the
356     * scheduling for the previous thread will be unaffected.
357     *
358     * @param newTid the tid of the thread to set, or 0 to unset the current thread.
359     * @param schedGroup the current scheduling group of the thread to set.
360     * @param suppressLogs {@code true} if any error logging should be disabled.
361     *
362     * @return the tid of the thread configured to run at the scheduling priority for VR
363     *          mode after this call completes (this may be the previous thread).
364     */
365    private int setVrRenderThreadLocked(int newTid, int schedGroup, boolean suppressLogs) {
366        boolean inVr = inVrMode();
367        boolean inPersistentVr = hasPersistentVrFlagSet();
368        if (!inVr || inPersistentVr || schedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
369            if (!suppressLogs) {
370               String reason = "caller is not the current top application.";
371               if (!inVr) {
372                   reason = "system not in VR mode.";
373               } else if (inPersistentVr) {
374                   reason = "system in persistent VR mode.";
375               }
376               Slog.w(TAG, "Failed to set VR thread, " + reason);
377            }
378            return mVrRenderThreadTid;
379        }
380        return updateVrRenderThreadLocked(newTid, suppressLogs);
381    }
382
383    /**
384     * Unset any special scheduling used for the current VR render thread, and return it to normal
385     * scheduling priority.
386     *
387     * @param suppressLogs {@code true} if any error logging should be disabled.
388     */
389    private void clearVrRenderThreadLocked(boolean suppressLogs) {
390        updateVrRenderThreadLocked(0, suppressLogs);
391    }
392
393    /**
394     * Check that the given tid is running in the process for the given pid, and throw an exception
395     * if not.
396     */
397    private void enforceThreadInProcess(int tid, int pid) {
398        if (!Process.isThreadInProcess(pid, tid)) {
399            throw new IllegalArgumentException("VR thread does not belong to process");
400        }
401    }
402
403    /**
404     * True when the system is in VR mode.
405     */
406    private boolean inVrMode() {
407        return (mVrState & FLAG_VR_MODE) != 0;
408    }
409
410    /**
411     * True when the persistent VR mode flag has been set.
412     *
413     * Note: Currently this does not necessarily mean that the system is in VR mode.
414     */
415    private boolean hasPersistentVrFlagSet() {
416        return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
417    }
418
419    @Override
420    public String toString() {
421      return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
422    }
423}
424