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