VrManagerService.java revision b56b72a8461dcc1a9dccd4c7019e2ecbf191fc1c
1/**
2 * Copyright (C) 2015 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 */
16package com.android.server.vr;
17
18import android.Manifest;
19import android.app.AppOpsManager;
20import android.app.NotificationManager;
21import android.annotation.NonNull;
22import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.pm.ApplicationInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.PackageManager.NameNotFoundException;
28import android.os.Binder;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.IInterface;
32import android.os.Looper;
33import android.os.Message;
34import android.os.RemoteCallbackList;
35import android.os.RemoteException;
36import android.os.UserHandle;
37import android.provider.Settings;
38import android.service.notification.NotificationListenerService;
39import android.service.vr.IVrListener;
40import android.service.vr.IVrManager;
41import android.service.vr.IVrStateCallbacks;
42import android.service.vr.VrListenerService;
43import android.util.ArraySet;
44import android.util.Slog;
45
46import com.android.internal.R;
47import com.android.server.SystemService;
48import com.android.server.utils.ManagedApplicationService.PendingEvent;
49import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
50import com.android.server.utils.ManagedApplicationService;
51import com.android.server.utils.ManagedApplicationService.BinderChecker;
52
53import java.io.FileDescriptor;
54import java.io.PrintWriter;
55import java.lang.StringBuilder;
56import java.lang.ref.WeakReference;
57import java.util.ArrayList;
58import java.util.Collection;
59import java.util.Objects;
60import java.util.Set;
61
62/**
63 * Service tracking whether VR mode is active, and notifying listening services of state changes.
64 * <p/>
65 * Services running in system server may modify the state of VrManagerService via the interface in
66 * VrManagerInternal, and may register to receive callbacks when the system VR mode changes via the
67 * interface given in VrStateListener.
68 * <p/>
69 * Device vendors may choose to receive VR state changes by implementing the VR mode HAL, e.g.:
70 *  hardware/libhardware/modules/vr
71 * <p/>
72 * In general applications may enable or disable VR mode by calling
73 * {@link android.app.Activity#setVrModeEnabled)}.  An application may also implement a service to
74 * be run while in VR mode by implementing {@link android.service.vr.VrListenerService}.
75 *
76 * @see {@link android.service.vr.VrListenerService}
77 * @see {@link com.android.server.vr.VrManagerInternal}
78 * @see {@link com.android.server.vr.VrStateListener}
79 *
80 * @hide
81 */
82public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
83
84    public static final String TAG = "VrManagerService";
85
86    public static final String VR_MANAGER_BINDER_SERVICE = "vrmanager";
87
88    private static final int PENDING_STATE_DELAY_MS = 300;
89
90    private static native void initializeNative();
91    private static native void setVrModeNative(boolean enabled);
92
93    private final Object mLock = new Object();
94
95    private final IBinder mOverlayToken = new Binder();
96
97    // State protected by mLock
98    private boolean mVrModeEnabled;
99    private EnabledComponentsObserver mComponentObserver;
100    private ManagedApplicationService mCurrentVrService;
101    private Context mContext;
102    private ComponentName mCurrentVrModeComponent;
103    private int mCurrentVrModeUser;
104    private boolean mWasDefaultGranted;
105    private boolean mGuard;
106    private final RemoteCallbackList<IVrStateCallbacks> mRemoteCallbacks =
107            new RemoteCallbackList<>();
108    private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
109    private String mPreviousNotificationPolicyAccessPackage;
110    private String mPreviousCoarseLocationPackage;
111    private String mPreviousManageOverlayPackage;
112    private VrState mPendingState;
113
114    private static final int MSG_VR_STATE_CHANGE = 0;
115    private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
116
117    private final Handler mHandler = new Handler() {
118        @Override
119        public void handleMessage(Message msg) {
120            switch(msg.what) {
121                case MSG_VR_STATE_CHANGE : {
122                    boolean state = (msg.arg1 == 1);
123                    int i = mRemoteCallbacks.beginBroadcast();
124                    while (i > 0) {
125                        i--;
126                        try {
127                            mRemoteCallbacks.getBroadcastItem(i).onVrStateChanged(state);
128                        } catch (RemoteException e) {
129                            // Noop
130                        }
131                    }
132                    mRemoteCallbacks.finishBroadcast();
133                } break;
134                case MSG_PENDING_VR_STATE_CHANGE : {
135                    synchronized(mLock) {
136                        VrManagerService.this.consumeAndApplyPendingStateLocked();
137                    }
138                } break;
139                default :
140                    throw new IllegalStateException("Unknown message type: " + msg.what);
141            }
142        }
143    };
144
145    private static class VrState {
146        final boolean enabled;
147        final int userId;
148        final ComponentName targetPackageName;
149        final ComponentName callingPackage;
150
151        VrState(boolean enabled, ComponentName targetPackageName, int userId,
152                ComponentName callingPackage) {
153            this.enabled = enabled;
154            this.userId = userId;
155            this.targetPackageName = targetPackageName;
156            this.callingPackage = callingPackage;
157        }
158    };
159
160    private static final BinderChecker sBinderChecker = new BinderChecker() {
161        @Override
162        public IInterface asInterface(IBinder binder) {
163            return IVrListener.Stub.asInterface(binder);
164        }
165
166        @Override
167        public boolean checkType(IInterface service) {
168            return service instanceof IVrListener;
169        }
170    };
171
172    /**
173     * Called when a user, package, or setting changes that could affect whether or not the
174     * currently bound VrListenerService is changed.
175     */
176    @Override
177    public void onEnabledComponentChanged() {
178        synchronized (mLock) {
179            if (mCurrentVrService == null) {
180                return; // No active services
181            }
182
183            // If there is a pending state change, we'd better deal with that first
184            consumeAndApplyPendingStateLocked();
185
186            if (mCurrentVrService == null) {
187                return; // No active services
188            }
189
190            // There is an active service, update it if needed
191            updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
192                    mCurrentVrService.getUserId(), null);
193        }
194    }
195
196    private final IVrManager mVrManager = new IVrManager.Stub() {
197
198        @Override
199        public void registerListener(IVrStateCallbacks cb) {
200            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
201            if (cb == null) {
202                throw new IllegalArgumentException("Callback binder object is null.");
203            }
204
205            VrManagerService.this.addStateCallback(cb);
206        }
207
208        @Override
209        public void unregisterListener(IVrStateCallbacks cb) {
210            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
211            if (cb == null) {
212                throw new IllegalArgumentException("Callback binder object is null.");
213            }
214
215            VrManagerService.this.removeStateCallback(cb);
216        }
217
218        @Override
219        public boolean getVrModeState() {
220            return VrManagerService.this.getVrMode();
221        }
222
223        @Override
224        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
225            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
226                    != PackageManager.PERMISSION_GRANTED) {
227                pw.println("permission denied: can't dump VrManagerService from pid="
228                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
229                return;
230            }
231            pw.print("mVrModeEnabled=");
232            pw.println(mVrModeEnabled);
233            pw.print("mCurrentVrModeUser=");
234            pw.println(mCurrentVrModeUser);
235            pw.print("mRemoteCallbacks=");
236            int i=mRemoteCallbacks.beginBroadcast(); // create the broadcast item array
237            while(i-->0) {
238                pw.print(mRemoteCallbacks.getBroadcastItem(i));
239                if (i>0) pw.print(", ");
240            }
241            mRemoteCallbacks.finishBroadcast();
242            pw.println();
243            pw.print("mCurrentVrService=");
244            pw.println(mCurrentVrService != null ? mCurrentVrService.getComponent() : "(none)");
245            pw.print("mCurrentVrModeComponent=");
246            pw.println(mCurrentVrModeComponent);
247        }
248
249    };
250
251    private void enforceCallerPermission(String permission) {
252        if (mContext.checkCallingOrSelfPermission(permission)
253                != PackageManager.PERMISSION_GRANTED) {
254            throw new SecurityException("Caller does not hold the permission " + permission);
255        }
256    }
257
258    /**
259     * Implementation of VrManagerInternal.  Callable only from system services.
260     */
261    private final class LocalService extends VrManagerInternal {
262        @Override
263        public void setVrMode(boolean enabled, ComponentName packageName, int userId,
264                ComponentName callingPackage) {
265            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
266        }
267
268        @Override
269        public boolean isCurrentVrListener(String packageName, int userId) {
270            return VrManagerService.this.isCurrentVrListener(packageName, userId);
271        }
272
273        @Override
274        public int hasVrPackage(ComponentName packageName, int userId) {
275            return VrManagerService.this.hasVrPackage(packageName, userId);
276        }
277    }
278
279    public VrManagerService(Context context) {
280        super(context);
281    }
282
283    @Override
284    public void onStart() {
285        synchronized(mLock) {
286            initializeNative();
287            mContext = getContext();
288        }
289
290        publishLocalService(VrManagerInternal.class, new LocalService());
291        publishBinderService(VR_MANAGER_BINDER_SERVICE, mVrManager.asBinder());
292    }
293
294    @Override
295    public void onBootPhase(int phase) {
296        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
297            synchronized (mLock) {
298                Looper looper = Looper.getMainLooper();
299                Handler handler = new Handler(looper);
300                ArrayList<EnabledComponentChangeListener> listeners = new ArrayList<>();
301                listeners.add(this);
302                mComponentObserver = EnabledComponentsObserver.build(mContext, handler,
303                        Settings.Secure.ENABLED_VR_LISTENERS, looper,
304                        android.Manifest.permission.BIND_VR_LISTENER_SERVICE,
305                        VrListenerService.SERVICE_INTERFACE, mLock, listeners);
306
307                mComponentObserver.rebuildAll();
308            }
309        }
310    }
311
312    @Override
313    public void onStartUser(int userHandle) {
314        synchronized (mLock) {
315            mComponentObserver.onUsersChanged();
316        }
317    }
318
319    @Override
320    public void onSwitchUser(int userHandle) {
321        synchronized (mLock) {
322            mComponentObserver.onUsersChanged();
323        }
324
325    }
326
327    @Override
328    public void onStopUser(int userHandle) {
329        synchronized (mLock) {
330            mComponentObserver.onUsersChanged();
331        }
332
333    }
334
335    @Override
336    public void onCleanupUser(int userHandle) {
337        synchronized (mLock) {
338            mComponentObserver.onUsersChanged();
339        }
340    }
341
342    private void updateOverlayStateLocked(ComponentName exemptedComponent) {
343        final long identity = Binder.clearCallingIdentity();
344        try {
345            AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
346            if (appOpsManager != null) {
347                String[] exemptions = (exemptedComponent == null) ? new String[0] :
348                        new String[] { exemptedComponent.getPackageName() };
349
350                appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
351                        mVrModeEnabled, mOverlayToken, exemptions);
352            }
353        } finally {
354            Binder.restoreCallingIdentity(identity);
355        }
356    }
357
358    /**
359     * Send VR mode changes (if the mode state has changed), and update the bound/unbound state of
360     * the currently selected VR listener service.  If the component selected for the VR listener
361     * service has changed, unbind the previous listener and bind the new listener (if enabled).
362     * <p/>
363     * Note: Must be called while holding {@code mLock}.
364     *
365     * @param enabled new state for VR mode.
366     * @param component new component to be bound as a VR listener.
367     * @param userId user owning the component to be bound.
368     * @param calling the component currently using VR mode, or null to leave unchanged.
369     *
370     * @return {@code true} if the component/user combination specified is valid.
371     */
372    private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component,
373            int userId, ComponentName calling) {
374
375        boolean sendUpdatedCaller = false;
376        final long identity = Binder.clearCallingIdentity();
377        try {
378
379            boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
380                    EnabledComponentsObserver.NO_ERROR);
381
382            // Always send mode change events.
383            changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
384
385            if (!enabled || !validUserComponent) {
386                // Unbind whatever is running
387                if (mCurrentVrService != null) {
388                    Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
389                            mCurrentVrService.getUserId());
390                    mCurrentVrService.disconnect();
391                    disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
392                            new UserHandle(mCurrentVrService.getUserId()));
393                    mCurrentVrService = null;
394                }
395            } else {
396                if (mCurrentVrService != null) {
397                    // Unbind any running service that doesn't match the component/user selection
398                    if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
399                        Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() +
400                                " for user " + mCurrentVrService.getUserId());
401                        disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
402                                new UserHandle(mCurrentVrService.getUserId()));
403                        createAndConnectService(component, userId);
404                        enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
405                                new UserHandle(mCurrentVrService.getUserId()));
406                        sendUpdatedCaller = true;
407                    }
408                    // The service with the correct component/user is bound
409                } else {
410                    // Nothing was previously running, bind a new service
411                    createAndConnectService(component, userId);
412                    enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
413                            new UserHandle(mCurrentVrService.getUserId()));
414                    sendUpdatedCaller = true;
415                }
416            }
417
418            if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent))  {
419                mCurrentVrModeComponent = calling;
420                mCurrentVrModeUser = userId;
421                sendUpdatedCaller = true;
422            }
423
424            if (mCurrentVrService != null && sendUpdatedCaller) {
425                final ComponentName c = mCurrentVrModeComponent;
426                mCurrentVrService.sendEvent(new PendingEvent() {
427                    @Override
428                    public void runEvent(IInterface service) throws RemoteException {
429                        IVrListener l = (IVrListener) service;
430                        l.focusedActivityChanged(c);
431                    }
432                });
433            }
434
435            return validUserComponent;
436        } finally {
437            Binder.restoreCallingIdentity(identity);
438        }
439    }
440
441    /**
442     * Enable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
443     * component package and user.
444     *
445     * @param component the component whose package should be enabled.
446     * @param userId the user that owns the given component.
447     */
448    private void enableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
449        if (mGuard) {
450            // Impossible
451            throw new IllegalStateException("Enabling permissions without disabling.");
452        }
453        mGuard = true;
454
455        PackageManager pm = mContext.getPackageManager();
456
457        String pName = component.getPackageName();
458        if (pm == null) {
459            Slog.e(TAG, "Couldn't set implied permissions for " + pName +
460                ", PackageManager isn't running");
461            return;
462        }
463
464        ApplicationInfo info = null;
465        try {
466            info = pm.getApplicationInfo(pName, PackageManager.GET_META_DATA);
467        } catch (NameNotFoundException e) {
468        }
469
470        if (info == null) {
471            Slog.e(TAG, "Couldn't set implied permissions for " + pName + ", no such package.");
472            return;
473        }
474
475        if (!(info.isSystemApp() || info.isUpdatedSystemApp())) {
476            return; // Application is not pre-installed, avoid setting implied permissions
477        }
478
479        mWasDefaultGranted = true;
480
481        grantCoarseLocationAccess(pName, userId);
482        grantOverlayAccess(pName, userId);
483        grantNotificationPolicyAccess(pName);
484        grantNotificationListenerAccess(pName, userId);
485    }
486
487    /**
488     * Disable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
489     * component package and user.
490     *
491     * @param component the component whose package should be disabled.
492     * @param userId the user that owns the given component.
493     */
494    private void disableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
495        if (!mGuard) {
496            // Impossible
497            throw new IllegalStateException("Disabling permissions without enabling.");
498        }
499        mGuard = false;
500
501        PackageManager pm = mContext.getPackageManager();
502
503        if (pm == null) {
504            Slog.e(TAG, "Couldn't remove implied permissions for " + component +
505                ", PackageManager isn't running");
506            return;
507        }
508
509        String pName = component.getPackageName();
510        if (mWasDefaultGranted) {
511            revokeCoarseLocationAccess(userId);
512            revokeOverlayAccess(userId);
513            revokeNotificationPolicyAccess(pName);
514            revokeNotificiationListenerAccess();
515            mWasDefaultGranted = false;
516        }
517
518    }
519
520    private void grantCoarseLocationAccess(String pkg, UserHandle userId) {
521        PackageManager pm = mContext.getPackageManager();
522        boolean prev = (PackageManager.PERMISSION_GRANTED ==
523                pm.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, pkg));
524        mPreviousCoarseLocationPackage = null;
525        if (!prev) {
526            pm.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_COARSE_LOCATION,
527                    userId);
528            mPreviousCoarseLocationPackage = pkg;
529        }
530    }
531
532    private void revokeCoarseLocationAccess(UserHandle userId) {
533        PackageManager pm = mContext.getPackageManager();
534        if (mPreviousCoarseLocationPackage != null) {
535            pm.revokeRuntimePermission(mPreviousCoarseLocationPackage,
536                    android.Manifest.permission.ACCESS_COARSE_LOCATION, userId);
537            mPreviousCoarseLocationPackage = null;
538        }
539    }
540
541    private void grantOverlayAccess(String pkg, UserHandle userId) {
542        PackageManager pm = mContext.getPackageManager();
543        boolean prev = (PackageManager.PERMISSION_GRANTED ==
544                pm.checkPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, pkg));
545        mPreviousManageOverlayPackage = null;
546        if (!prev) {
547            pm.grantRuntimePermission(pkg, android.Manifest.permission.SYSTEM_ALERT_WINDOW,
548                    userId);
549            mPreviousManageOverlayPackage = pkg;
550        }
551    }
552
553    private void revokeOverlayAccess(UserHandle userId) {
554        PackageManager pm = mContext.getPackageManager();
555        if (mPreviousManageOverlayPackage != null) {
556            pm.revokeRuntimePermission(mPreviousManageOverlayPackage,
557                    android.Manifest.permission.SYSTEM_ALERT_WINDOW, userId);
558            mPreviousManageOverlayPackage = null;
559        }
560    }
561
562    private void grantNotificationPolicyAccess(String pkg) {
563        NotificationManager nm = mContext.getSystemService(NotificationManager.class);
564        boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg);
565        mPreviousNotificationPolicyAccessPackage = null;
566        if (!prev) {
567            mPreviousNotificationPolicyAccessPackage = pkg;
568            nm.setNotificationPolicyAccessGranted(pkg, true);
569        }
570    }
571
572    private void revokeNotificationPolicyAccess(String pkg) {
573        NotificationManager nm = mContext.getSystemService(NotificationManager.class);
574        if (mPreviousNotificationPolicyAccessPackage != null) {
575            if (mPreviousNotificationPolicyAccessPackage.equals(pkg)) {
576                // Remove any DND zen rules possibly created by the package.
577                nm.removeAutomaticZenRules(mPreviousNotificationPolicyAccessPackage);
578                // Remove Notification Policy Access.
579                nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
580                mPreviousNotificationPolicyAccessPackage = null;
581            } else {
582                Slog.e(TAG, "Couldn't remove Notification Policy Access for package: " + pkg);
583            }
584        }
585    }
586
587    private void grantNotificationListenerAccess(String pkg, UserHandle userId) {
588        PackageManager pm = mContext.getPackageManager();
589        ArraySet<ComponentName> possibleServices = EnabledComponentsObserver.loadComponentNames(pm,
590                userId.getIdentifier(), NotificationListenerService.SERVICE_INTERFACE,
591                android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
592        ContentResolver resolver = mContext.getContentResolver();
593
594        ArraySet<String> current = getCurrentNotifListeners(resolver);
595
596        mPreviousToggledListenerSettings.clear();
597
598        for (ComponentName c : possibleServices) {
599            String flatName = c.flattenToString();
600            if (Objects.equals(c.getPackageName(), pkg)
601                    && !current.contains(flatName)) {
602                mPreviousToggledListenerSettings.add(flatName);
603                current.add(flatName);
604            }
605        }
606
607        if (current.size() > 0) {
608            String flatSettings = formatSettings(current);
609            Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
610                    flatSettings);
611        }
612    }
613
614    private void revokeNotificiationListenerAccess() {
615        if (mPreviousToggledListenerSettings.isEmpty()) {
616            return;
617        }
618
619        ContentResolver resolver = mContext.getContentResolver();
620        ArraySet<String> current = getCurrentNotifListeners(resolver);
621
622        current.removeAll(mPreviousToggledListenerSettings);
623        mPreviousToggledListenerSettings.clear();
624
625        String flatSettings = formatSettings(current);
626        Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
627                flatSettings);
628    }
629
630    private ArraySet<String> getCurrentNotifListeners(ContentResolver resolver) {
631        String flat = Settings.Secure.getString(resolver,
632                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
633
634        ArraySet<String> current = new ArraySet<>();
635        if (flat != null) {
636            String[] allowed = flat.split(":");
637            for (String s : allowed) {
638                current.add(s);
639            }
640        }
641        return current;
642    }
643
644    private static String formatSettings(Collection<String> c) {
645        if (c == null || c.isEmpty()) {
646            return "";
647        }
648
649        StringBuilder b = new StringBuilder();
650        boolean start = true;
651        for (String s : c) {
652            if ("".equals(s)) {
653                continue;
654            }
655            if (!start) {
656                b.append(':');
657            }
658            b.append(s);
659            start = false;
660        }
661        return b.toString();
662    }
663
664
665
666    private void createAndConnectService(@NonNull ComponentName component, int userId) {
667        mCurrentVrService = VrManagerService.create(mContext, component, userId);
668        mCurrentVrService.connect();
669        Slog.i(TAG, "Connecting " + component + " for user " + userId);
670    }
671
672    /**
673     * Send VR mode change callbacks to HAL and system services if mode has actually changed.
674     * <p/>
675     * Note: Must be called while holding {@code mLock}.
676     *
677     * @param enabled new state of the VR mode.
678     * @param exemptedComponent a component to exempt from AppOps restrictions for overlays.
679     */
680    private void changeVrModeLocked(boolean enabled, ComponentName exemptedComponent) {
681        if (mVrModeEnabled != enabled) {
682            mVrModeEnabled = enabled;
683
684            // Log mode change event.
685            Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));
686            setVrModeNative(mVrModeEnabled);
687
688            updateOverlayStateLocked(exemptedComponent);
689            onVrModeChangedLocked();
690        }
691    }
692
693    /**
694     * Notify system services of VR mode change.
695     * <p/>
696     * Note: Must be called while holding {@code mLock}.
697     */
698    private void onVrModeChangedLocked() {
699        mHandler.sendMessage(mHandler.obtainMessage(MSG_VR_STATE_CHANGE,
700                (mVrModeEnabled) ? 1 : 0, 0));
701    }
702
703    /**
704     * Helper function for making ManagedApplicationService instances.
705     */
706    private static ManagedApplicationService create(@NonNull Context context,
707            @NonNull ComponentName component, int userId) {
708        return ManagedApplicationService.build(context, component, userId,
709                R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS,
710                sBinderChecker);
711    }
712
713    private void consumeAndApplyPendingStateLocked() {
714        if (mPendingState != null) {
715            updateCurrentVrServiceLocked(mPendingState.enabled,
716                    mPendingState.targetPackageName, mPendingState.userId,
717                    mPendingState.callingPackage);
718            mPendingState = null;
719        }
720    }
721
722    /*
723     * Implementation of VrManagerInternal calls.  These are callable from system services.
724     */
725
726    private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
727            int userId, @NonNull ComponentName callingPackage) {
728
729        synchronized (mLock) {
730
731            if (!enabled && mCurrentVrService != null) {
732                // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
733                // and service bind/unbind in case we are immediately switching to another VR app.
734                if (mPendingState == null) {
735                    mHandler.sendEmptyMessageDelayed(MSG_PENDING_VR_STATE_CHANGE,
736                            PENDING_STATE_DELAY_MS);
737                }
738
739                mPendingState = new VrState(enabled, targetPackageName, userId, callingPackage);
740                return;
741            } else {
742                mHandler.removeMessages(MSG_PENDING_VR_STATE_CHANGE);
743                mPendingState = null;
744            }
745
746            updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage);
747        }
748    }
749
750    private int hasVrPackage(@NonNull ComponentName targetPackageName, int userId) {
751        synchronized (mLock) {
752            return mComponentObserver.isValid(targetPackageName, userId);
753        }
754    }
755
756    private boolean isCurrentVrListener(String packageName, int userId) {
757        synchronized (mLock) {
758            if (mCurrentVrService == null) {
759                return false;
760            }
761            return mCurrentVrService.getComponent().getPackageName().equals(packageName) &&
762                    userId == mCurrentVrService.getUserId();
763        }
764    }
765
766    /*
767     * Implementation of IVrManager calls.
768     */
769
770    private void addStateCallback(IVrStateCallbacks cb) {
771        mRemoteCallbacks.register(cb);
772    }
773
774    private void removeStateCallback(IVrStateCallbacks cb) {
775        mRemoteCallbacks.unregister(cb);
776    }
777
778    private boolean getVrMode() {
779        synchronized (mLock) {
780            return mVrModeEnabled;
781        }
782    }
783}
784