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 com.android.server.LocalServices;
     24 import 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  */
     44 final 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