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