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
17package com.android.media.tv.remoteprovider;
18
19import android.content.Context;
20import android.media.tv.ITvRemoteProvider;
21import android.media.tv.ITvRemoteServiceInput;
22import android.os.Handler;
23import android.os.IBinder;
24import android.os.Looper;
25import android.os.Message;
26import android.os.RemoteException;
27import 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
41public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
273        public void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) {
274            mHandler.obtainMessage(MSG_SET_SERVICE_INPUT, tvServiceInput).sendToTarget();
275        }
276
277        @Override
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 {
285        public ProviderHandler(Looper looper) {
286            super(looper, null, true);
287        }
288
289        @Override
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