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 android.util.proto.ProtoOutputStream;
24import android.util.proto.ProtoUtils;
25
26import com.android.server.LocalServices;
27import 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 */
47final 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     */
131    public VrController(final Object globalAmLock) {
132        mGlobalAmLock = globalAmLock;
133    }
134
135    /**
136     * Called when ActivityManagerService receives its systemReady call during boot.
137     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
430    private boolean hasPersistentVrFlagSet() {
431        return (mVrState & FLAG_PERSISTENT_VR_MODE) != 0;
432    }
433
434    @Override
435    public String toString() {
436      return String.format("[VrState=0x%x,VrRenderThreadTid=%d]", mVrState, mVrRenderThreadTid);
437    }
438
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