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        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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
410    private boolean hasPersistentVrFlagSet() {
411        return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
412    }
413
414    @Override
415    public String toString() {
416      return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
417    }
418}
419