Home | History | Annotate | Download | only in am
      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      */
    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