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 com.android.media.tv.remoteprovider;
18 
19 import android.content.Context;
20 import android.media.tv.ITvRemoteProvider;
21 import android.media.tv.ITvRemoteServiceInput;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.Looper;
25 import android.os.Message;
26 import android.os.RemoteException;
27 import android.util.Log;
28 
29 /**
30  * Base class for emote providers implemented in unbundled service.
31  * <p/>
32  * This object is not thread safe.  It is only intended to be accessed on the
33  * {@link Context#getMainLooper main looper thread} of an application.
34  * </p><p>
35  * IMPORTANT: This class is effectively a system API for unbundled emote service, and
36  * must remain API stable. See README.txt in the root of this package for more information.
37  * </p>
38  */
39 
40 
41 public abstract class TvRemoteProvider {
42 
43     /**
44      * The {@link Intent} that must be declared as handled by the service.
45      * The service must also require the {@link android.Manifest.permission#BIND_TV_REMOTE_SERVICE}
46      * permission so that other applications cannot abuse it.
47      */
48     public static final String SERVICE_INTERFACE =
49             "com.android.media.tv.remoteprovider.TvRemoteProvider";
50 
51     private static final String TAG = "TvRemoteProvider";
52     private static final boolean DEBUG_KEYS = false;
53     private static final int MSG_SET_SERVICE_INPUT = 1;
54     private static final int MSG_SEND_INPUTBRIDGE_CONNECTED = 2;
55     private final Context mContext;
56     private final ProviderStub mStub;
57     private final ProviderHandler mHandler;
58     private ITvRemoteServiceInput mRemoteServiceInput;
59 
60     /**
61      * Creates a provider for an unbundled emote controller
62      * service allowing it to interface with the tv remote controller
63      * system service.
64      *
65      * @param context The application context for the remote provider.
66      */
TvRemoteProvider(Context context)67     public TvRemoteProvider(Context context) {
68         mContext = context.getApplicationContext();
69         mStub = new ProviderStub();
70         mHandler = new ProviderHandler(mContext.getMainLooper());
71     }
72 
73     /**
74      * Gets the context of the remote service provider.
75      */
getContext()76     public final Context getContext() {
77         return mContext;
78     }
79 
80 
81     /**
82      * Gets the Binder associated with the provider.
83      * <p>
84      * This is intended to be used for the onBind() method of a service that implements
85      * a remote provider service.
86      * </p>
87      *
88      * @return The IBinder instance associated with the provider.
89      */
getBinder()90     public IBinder getBinder() {
91         return mStub;
92     }
93 
94     /**
95      * Information about the InputBridge connected status.
96      *
97      * @param token Identifier for the connection. Null, if failed.
98      */
onInputBridgeConnected(IBinder token)99     public void onInputBridgeConnected(IBinder token) {
100     }
101 
102     /**
103      * Set a sink for sending events to framework service.
104      *
105      * @param tvServiceInput sink defined in framework service
106      */
setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput)107     private void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) {
108         mRemoteServiceInput = tvServiceInput;
109     }
110 
111     /**
112      * openRemoteInputBridge : Open an input bridge for a particular device.
113      * Clients should pass in a token that can be used to match this request with a token that
114      * will be returned by {@link TvRemoteProvider#onInputBridgeConnected(IBinder token)}
115      * <p>
116      * The token should be used for subsequent calls.
117      * </p>
118      *
119      * @param name        Device name
120      * @param token       Identifier for this connection
121      * @param width       Width of the device's virtual touchpad
122      * @param height      Height of the device's virtual touchpad
123      * @param maxPointers Maximum supported pointers
124      * @throws RuntimeException
125      */
openRemoteInputBridge(IBinder token, String name, int width, int height, int maxPointers)126     public void openRemoteInputBridge(IBinder token, String name, int width, int height,
127                                       int maxPointers) throws RuntimeException {
128         try {
129             mRemoteServiceInput.openInputBridge(token, name, width, height, maxPointers);
130         } catch (RemoteException re) {
131             throw re.rethrowFromSystemServer();
132         }
133     }
134 
135     /**
136      * closeInputBridge : Close input bridge for a device
137      *
138      * @param token identifier for this connection
139      * @throws RuntimeException
140      */
closeInputBridge(IBinder token)141     public void closeInputBridge(IBinder token) throws RuntimeException {
142         try {
143             mRemoteServiceInput.closeInputBridge(token);
144         } catch (RemoteException re) {
145             throw re.rethrowFromSystemServer();
146         }
147     }
148 
149     /**
150      * clearInputBridge : Clear out any existing key or pointer events in queue for this device by
151      *                    dropping them on the floor and sending an UP to all keys and pointer
152      *                    slots.
153      *
154      * @param token identifier for this connection
155      * @throws RuntimeException
156      */
clearInputBridge(IBinder token)157     public void clearInputBridge(IBinder token) throws RuntimeException {
158         if (DEBUG_KEYS) Log.d(TAG, "clearInputBridge() token " + token);
159         try {
160             mRemoteServiceInput.clearInputBridge(token);
161         } catch (RemoteException re) {
162             throw re.rethrowFromSystemServer();
163         }
164     }
165 
166     /**
167      * sendTimestamp : Send a timestamp for a set of pointer events
168      *
169      * @param token     identifier for the device
170      * @param timestamp Timestamp to be used in
171      *                  {@link android.os.SystemClock#uptimeMillis} time base
172      * @throws RuntimeException
173      */
sendTimestamp(IBinder token, long timestamp)174     public void sendTimestamp(IBinder token, long timestamp) throws RuntimeException {
175         if (DEBUG_KEYS) Log.d(TAG, "sendTimestamp() token: " + token +
176                 ", timestamp: " + timestamp);
177         try {
178             mRemoteServiceInput.sendTimestamp(token, timestamp);
179         } catch (RemoteException re) {
180             throw re.rethrowFromSystemServer();
181         }
182     }
183 
184     /**
185      * sendKeyUp : Send key up event for a device
186      *
187      * @param token   identifier for this connection
188      * @param keyCode Key code to be sent
189      * @throws RuntimeException
190      */
sendKeyUp(IBinder token, int keyCode)191     public void sendKeyUp(IBinder token, int keyCode) throws RuntimeException {
192         if (DEBUG_KEYS) Log.d(TAG, "sendKeyUp() token: " + token + ", keyCode: " + keyCode);
193         try {
194             mRemoteServiceInput.sendKeyUp(token, keyCode);
195         } catch (RemoteException re) {
196             throw re.rethrowFromSystemServer();
197         }
198     }
199 
200     /**
201      * sendKeyDown : Send key down event for a device
202      *
203      * @param token   identifier for this connection
204      * @param keyCode Key code to be sent
205      * @throws RuntimeException
206      */
sendKeyDown(IBinder token, int keyCode)207     public void sendKeyDown(IBinder token, int keyCode) throws RuntimeException {
208         if (DEBUG_KEYS) Log.d(TAG, "sendKeyDown() token: " + token +
209                 ", keyCode: " + keyCode);
210         try {
211             mRemoteServiceInput.sendKeyDown(token, keyCode);
212         } catch (RemoteException re) {
213             throw re.rethrowFromSystemServer();
214         }
215     }
216 
217     /**
218      * sendPointerUp : Send pointer up event for a device
219      *
220      * @param token     identifier for the device
221      * @param pointerId Pointer id to be used. Value may be from 0
222      *                  to {@link MotionEvent#getPointerCount()} -1
223      * @throws RuntimeException
224      */
sendPointerUp(IBinder token, int pointerId)225     public void sendPointerUp(IBinder token, int pointerId) throws RuntimeException {
226         if (DEBUG_KEYS) Log.d(TAG, "sendPointerUp() token: " + token +
227                 ", pointerId: " + pointerId);
228         try {
229             mRemoteServiceInput.sendPointerUp(token, pointerId);
230         } catch (RemoteException re) {
231             throw re.rethrowFromSystemServer();
232         }
233     }
234 
235     /**
236      * sendPointerDown : Send pointer down event for a device
237      *
238      * @param token     identifier for the device
239      * @param pointerId Pointer id to be used. Value may be from 0
240      *                  to {@link MotionEvent#getPointerCount()} -1
241      * @param x         X co-ordinates in display pixels
242      * @param y         Y co-ordinates in display pixels
243      * @throws RuntimeException
244      */
sendPointerDown(IBinder token, int pointerId, int x, int y)245     public void sendPointerDown(IBinder token, int pointerId, int x, int y)
246             throws RuntimeException {
247         if (DEBUG_KEYS) Log.d(TAG, "sendPointerDown() token: " + token +
248                 ", pointerId: " + pointerId);
249         try {
250             mRemoteServiceInput.sendPointerDown(token, pointerId, x, y);
251         } catch (RemoteException re) {
252             throw re.rethrowFromSystemServer();
253         }
254     }
255 
256     /**
257      * sendPointerSync : Send pointer sync event for a device
258      *
259      * @param token identifier for the device
260      * @throws RuntimeException
261      */
sendPointerSync(IBinder token)262     public void sendPointerSync(IBinder token) throws RuntimeException {
263         if (DEBUG_KEYS) Log.d(TAG, "sendPointerSync() token: " + token);
264         try {
265             mRemoteServiceInput.sendPointerSync(token);
266         } catch (RemoteException re) {
267             throw re.rethrowFromSystemServer();
268         }
269     }
270 
271     private final class ProviderStub extends ITvRemoteProvider.Stub {
272         @Override
setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput)273         public void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) {
274             mHandler.obtainMessage(MSG_SET_SERVICE_INPUT, tvServiceInput).sendToTarget();
275         }
276 
277         @Override
onInputBridgeConnected(IBinder token)278         public void onInputBridgeConnected(IBinder token) {
279             mHandler.obtainMessage(MSG_SEND_INPUTBRIDGE_CONNECTED, 0, 0,
280                     (IBinder) token).sendToTarget();
281         }
282     }
283 
284     private final class ProviderHandler extends Handler {
ProviderHandler(Looper looper)285         public ProviderHandler(Looper looper) {
286             super(looper, null, true);
287         }
288 
289         @Override
handleMessage(Message msg)290         public void handleMessage(Message msg) {
291             switch (msg.what) {
292                 case MSG_SET_SERVICE_INPUT: {
293                     setRemoteServiceInputSink((ITvRemoteServiceInput) msg.obj);
294                     break;
295                 }
296                 case MSG_SEND_INPUTBRIDGE_CONNECTED: {
297                     onInputBridgeConnected((IBinder) msg.obj);
298                     break;
299                 }
300             }
301         }
302     }
303 }
304