1 /**
2  * Copyright (C) 2016 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 android.service.vr;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SdkConstant;
21 import android.app.ActivityManager;
22 import android.app.Service;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.Looper;
30 import android.os.Message;
31 
32 /**
33  * A service that is bound from the system while running in virtual reality (VR) mode.
34  *
35  * <p>To extend this class, you must declare the service in your manifest file with
36  * the {@link android.Manifest.permission#BIND_VR_LISTENER_SERVICE} permission
37  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
38  * <pre>
39  * &lt;service android:name=".VrListener"
40  *          android:label="&#64;string/service_name"
41  *          android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
42  *     &lt;intent-filter>
43  *         &lt;action android:name="android.service.vr.VrListenerService" />
44  *     &lt;/intent-filter>
45  * &lt;/service>
46  * </pre>
47  *
48  * <p>This service is bound when the system enters VR mode and is unbound when the system leaves VR
49  * mode.</p>
50  * <p>The system will enter VR mode when an application that has previously called
51  * {@link android.app.Activity#setVrModeEnabled} gains user focus.  The system will only start this
52  * service if the VR application has specifically targeted this service by specifying
53  * its {@link ComponentName} in the call to {@link android.app.Activity#setVrModeEnabled} and if
54  * this service is installed and enabled in the current user's settings.</p>
55  *
56  * @see android.provider.Settings#ACTION_VR_LISTENER_SETTINGS
57  * @see android.app.Activity#setVrModeEnabled
58  * @see android.R.attr#enableVrMode
59  */
60 public abstract class VrListenerService extends Service {
61 
62     /**
63      * The {@link Intent} that must be declared as handled by the service.
64      */
65     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
66     public static final String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
67 
68     private final Handler mHandler;
69 
70     private static final int MSG_ON_CURRENT_VR_ACTIVITY_CHANGED = 1;
71 
72     private final IVrListener.Stub mBinder = new IVrListener.Stub() {
73         @Override
74         public void focusedActivityChanged(
75                 ComponentName component, boolean running2dInVr, int pid) {
76             mHandler.obtainMessage(MSG_ON_CURRENT_VR_ACTIVITY_CHANGED, running2dInVr ? 1 : 0,
77                     pid, component).sendToTarget();
78         }
79     };
80 
81     private final class VrListenerHandler extends Handler {
VrListenerHandler(Looper looper)82         public VrListenerHandler(Looper looper) {
83             super(looper);
84         }
85 
86         @Override
handleMessage(Message msg)87         public void handleMessage(Message msg) {
88             switch (msg.what) {
89                 case MSG_ON_CURRENT_VR_ACTIVITY_CHANGED: {
90                     VrListenerService.this.onCurrentVrActivityChanged(
91                             (ComponentName) msg.obj, msg.arg1 == 1, msg.arg2);
92                 } break;
93             }
94         }
95     }
96 
97     @Override
onBind(Intent intent)98     public IBinder onBind(Intent intent) {
99         return mBinder;
100     }
101 
VrListenerService()102     public VrListenerService() {
103         mHandler = new VrListenerHandler(Looper.getMainLooper());
104     }
105 
106     /**
107      * Called when the current activity using VR mode has changed.
108      *
109      * <p>This will be called when this service is initially bound, but is not
110      * guaranteed to be called before onUnbind.  In general, this is intended to be used to
111      * determine when user focus has transitioned between two VR activities.  If both activities
112      * have declared {@link android.R.attr#enableVrMode} with this service (and this
113      * service is present and enabled), this service will not be unbound during the activity
114      * transition.</p>
115      *
116      * @param component the {@link ComponentName} of the VR activity that the system has
117      *    switched to, or null if the system is displaying a 2D activity in VR compatibility mode.
118      *
119      * @see android.app.Activity#setVrModeEnabled
120      * @see android.R.attr#enableVrMode
121      */
onCurrentVrActivityChanged(ComponentName component)122     public void onCurrentVrActivityChanged(ComponentName component) {
123         // Override to implement
124     }
125 
126     /**
127      * An extended version of onCurrentVrActivityChanged
128      *
129      * <p>This will be called when this service is initially bound, but is not
130      * guaranteed to be called before onUnbind.  In general, this is intended to be used to
131      * determine when user focus has transitioned between two VR activities, or between a
132      * VR activity and a 2D activity. This should be overridden instead of the above
133      * onCurrentVrActivityChanged as that version is deprecated.</p>
134      *
135      * @param component the {@link ComponentName} of the VR activity or the 2D intent.
136      * @param running2dInVr true if the component is a 2D component.
137      * @param pid the process the component is running in.
138      *
139      * @see android.app.Activity#setVrModeEnabled
140      * @see android.R.attr#enableVrMode
141      * @hide
142      */
143     @UnsupportedAppUsage
onCurrentVrActivityChanged( ComponentName component, boolean running2dInVr, int pid)144     public void onCurrentVrActivityChanged(
145             ComponentName component, boolean running2dInVr, int pid) {
146         // Override to implement. Default to old behaviour of sending null for 2D.
147         onCurrentVrActivityChanged(running2dInVr ? null : component);
148     }
149 
150     /**
151      * Checks if the given component is enabled in user settings.
152      *
153      * <p>If this component is not enabled in the user's settings, it will not be started when
154      * the system enters VR mode.  The user interface for enabling VrListenerService components
155      * can be started by sending the {@link android.provider.Settings#ACTION_VR_LISTENER_SETTINGS}
156      * intent.</p>
157      *
158      * @param context the {@link Context} to use for looking up the requested component.
159      * @param requestedComponent the name of the component that implements
160      * {@link android.service.vr.VrListenerService} to check.
161      *
162      * @return {@code true} if this component is enabled in settings.
163      *
164      * @see android.provider.Settings#ACTION_VR_LISTENER_SETTINGS
165      */
isVrModePackageEnabled(@onNull Context context, @NonNull ComponentName requestedComponent)166     public static final boolean isVrModePackageEnabled(@NonNull Context context,
167             @NonNull ComponentName requestedComponent) {
168         ActivityManager am = context.getSystemService(ActivityManager.class);
169         if (am == null) {
170             return false;
171         }
172         return am.isVrModePackageEnabled(requestedComponent);
173     }
174 }
175