blob: 961c9925414abc5313bdd2151dedfaa360746bdd [file] [log] [blame]
Sujith Ramakrishnanb5b86c12016-01-28 16:53:16 -08001/*
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.server.tv;
18
19import android.content.Context;
20import android.os.Handler;
21import android.os.IBinder;
22import android.os.Looper;
23import android.os.Message;
24import android.util.ArrayMap;
25import android.util.Slog;
26
27import com.android.server.SystemService;
28import com.android.server.Watchdog;
29
30import java.io.IOException;
31import java.util.ArrayList;
32import java.util.Map;
33
34/**
35 * TvRemoteService represents a system service that allows a connected
36 * remote control (emote) service to inject white-listed input events
37 * and call other specified methods for functioning as an emote service.
38 * <p/>
39 * This service is intended for use only by white-listed packages.
40 */
41public class TvRemoteService extends SystemService implements Watchdog.Monitor {
42 private static final String TAG = "TvRemoteService";
43 private static final boolean DEBUG = false;
44 private static final boolean DEBUG_KEYS = false;
45
46 private Map<IBinder, UinputBridge> mBridgeMap = new ArrayMap();
47 private Map<IBinder, TvRemoteProviderProxy> mProviderMap = new ArrayMap();
48 private ArrayList<TvRemoteProviderProxy> mProviderList = new ArrayList<>();
49
50 /**
51 * State guarded by mLock.
52 * This is the second lock in sequence for an incoming call.
53 * The first lock is always {@link TvRemoteProviderProxy#mLock}
54 *
55 * There are currently no methods that break this sequence.
56 * Special note:
57 * Outgoing call informInputBridgeConnected(), which is called from
58 * openInputBridgeInternalLocked() uses a handler thereby relinquishing held locks.
59 */
60 private final Object mLock = new Object();
61
62 public final UserHandler mHandler;
63
64 public TvRemoteService(Context context) {
65 super(context);
66 mHandler = new UserHandler(new UserProvider(TvRemoteService.this), context);
67 Watchdog.getInstance().addMonitor(this);
68 }
69
70 @Override
71 public void onStart() {
72 if (DEBUG) Slog.d(TAG, "onStart()");
73 }
74
75 @Override
76 public void monitor() {
77 synchronized (mLock) { /* check for deadlock */ }
78 }
79
80 @Override
81 public void onBootPhase(int phase) {
82 if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
83 if (DEBUG) Slog.d(TAG, "PHASE_THIRD_PARTY_APPS_CAN_START");
84 mHandler.sendEmptyMessage(UserHandler.MSG_START);
85 }
86 }
87
88 //Outgoing calls.
89 private void informInputBridgeConnected(IBinder token) {
90 mHandler.obtainMessage(UserHandler.MSG_INPUT_BRIDGE_CONNECTED, 0, 0, token).sendToTarget();
91 }
92
93 // Incoming calls.
94 private void openInputBridgeInternalLocked(TvRemoteProviderProxy provider, IBinder token,
95 String name, int width, int height,
96 int maxPointers) {
97 if (DEBUG) {
98 Slog.d(TAG, "openInputBridgeInternalLocked(), token: " + token + ", name: " + name +
99 ", width: " + width + ", height: " + height + ", maxPointers: " + maxPointers);
100 }
101
102 try {
103 //Create a new bridge, if one does not exist already
104 if (mBridgeMap.containsKey(token)) {
105 if (DEBUG) Slog.d(TAG, "RemoteBridge already exists");
106 // Respond back with success.
107 informInputBridgeConnected(token);
108 return;
109 }
110
111 UinputBridge inputBridge = new UinputBridge(token, name, width, height, maxPointers);
112
113 mBridgeMap.put(token, inputBridge);
114 mProviderMap.put(token, provider);
115
116 // Respond back with success.
117 informInputBridgeConnected(token);
118
119 } catch (IOException ioe) {
120 Slog.e(TAG, "Cannot create device for " + name);
121 }
122 }
123
124 private void closeInputBridgeInternalLocked(IBinder token) {
125 if (DEBUG) {
126 Slog.d(TAG, "closeInputBridgeInternalLocked(), token: " + token);
127 }
128
129 // Close an existing RemoteBridge
130 UinputBridge inputBridge = mBridgeMap.get(token);
131 if (inputBridge != null) {
132 inputBridge.close(token);
133 }
134
135 mBridgeMap.remove(token);
136 }
137
138
139 private void clearInputBridgeInternalLocked(IBinder token) {
140 if (DEBUG) {
141 Slog.d(TAG, "clearInputBridgeInternalLocked(), token: " + token);
142 }
143
144 UinputBridge inputBridge = mBridgeMap.get(token);
145 if (inputBridge != null) {
146 inputBridge.clear(token);
147 }
148 }
149
150 private void sendTimeStampInternalLocked(IBinder token, long timestamp) {
151 UinputBridge inputBridge = mBridgeMap.get(token);
152 if (inputBridge != null) {
153 inputBridge.sendTimestamp(token, timestamp);
154 }
155 }
156
157 private void sendKeyDownInternalLocked(IBinder token, int keyCode) {
158 if (DEBUG_KEYS) {
159 Slog.d(TAG, "sendKeyDownInternalLocked(), token: " + token + ", keyCode: " + keyCode);
160 }
161
162 UinputBridge inputBridge = mBridgeMap.get(token);
163 if (inputBridge != null) {
164 inputBridge.sendKeyDown(token, keyCode);
165 }
166 }
167
168 private void sendKeyUpInternalLocked(IBinder token, int keyCode) {
169 if (DEBUG_KEYS) {
170 Slog.d(TAG, "sendKeyUpInternalLocked(), token: " + token + ", keyCode: " + keyCode);
171 }
172
173 UinputBridge inputBridge = mBridgeMap.get(token);
174 if (inputBridge != null) {
175 inputBridge.sendKeyUp(token, keyCode);
176 }
177 }
178
179 private void sendPointerDownInternalLocked(IBinder token, int pointerId, int x, int y) {
180 if (DEBUG_KEYS) {
181 Slog.d(TAG, "sendPointerDownInternalLocked(), token: " + token + ", pointerId: " +
182 pointerId + ", x: " + x + ", y: " + y);
183 }
184
185 UinputBridge inputBridge = mBridgeMap.get(token);
186 if (inputBridge != null) {
187 inputBridge.sendPointerDown(token, pointerId, x, y);
188 }
189 }
190
191 private void sendPointerUpInternalLocked(IBinder token, int pointerId) {
192 if (DEBUG_KEYS) {
193 Slog.d(TAG, "sendPointerUpInternalLocked(), token: " + token + ", pointerId: " +
194 pointerId);
195 }
196
197 UinputBridge inputBridge = mBridgeMap.get(token);
198 if (inputBridge != null) {
199 inputBridge.sendPointerUp(token, pointerId);
200 }
201 }
202
203 private void sendPointerSyncInternalLocked(IBinder token) {
204 if (DEBUG_KEYS) {
205 Slog.d(TAG, "sendPointerSyncInternalLocked(), token: " + token);
206 }
207
208 UinputBridge inputBridge = mBridgeMap.get(token);
209 if (inputBridge != null) {
210 inputBridge.sendPointerSync(token);
211 }
212 }
213
214 private final class UserHandler extends Handler {
215
216 public static final int MSG_START = 1;
217 public static final int MSG_INPUT_BRIDGE_CONNECTED = 2;
218
219 private final TvRemoteProviderWatcher mWatcher;
220 private boolean mRunning;
221
222 public UserHandler(UserProvider provider, Context context) {
223 super(Looper.getMainLooper(), null, true);
224 mWatcher = new TvRemoteProviderWatcher(context, provider, this);
225 }
226
227 @Override
228 public void handleMessage(Message msg) {
229 switch (msg.what) {
230 case MSG_START: {
231 start();
232 break;
233 }
234 case MSG_INPUT_BRIDGE_CONNECTED: {
235 IBinder token = (IBinder) msg.obj;
236 TvRemoteProviderProxy provider = mProviderMap.get(token);
237 if (provider != null) {
238 provider.inputBridgeConnected(token);
239 }
240 break;
241 }
242 }
243 }
244
245 private void start() {
246 if (!mRunning) {
247 mRunning = true;
248 mWatcher.start(); // also starts all providers
249 }
250 }
251 }
252
253 private final class UserProvider implements TvRemoteProviderWatcher.ProviderMethods,
254 TvRemoteProviderProxy.ProviderMethods {
255
256 private final TvRemoteService mService;
257
258 public UserProvider(TvRemoteService service) {
259 mService = service;
260 }
261
262 @Override
263 public void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
264 int width, int height, int maxPointers) {
265 if (DEBUG) {
266 Slog.d(TAG, "openInputBridge(), token: " + token +
267 ", name: " + name + ", width: " + width +
268 ", height: " + height + ", maxPointers: " + maxPointers);
269 }
270
271 synchronized (mLock) {
272 if (mProviderList.contains(provider)) {
273 mService.openInputBridgeInternalLocked(provider, token, name, width, height,
274 maxPointers);
275 }
276 }
277 }
278
279 @Override
280 public void closeInputBridge(TvRemoteProviderProxy provider, IBinder token) {
281 if (DEBUG) Slog.d(TAG, "closeInputBridge(), token: " + token);
282 synchronized (mLock) {
283 if (mProviderList.contains(provider)) {
284 mService.closeInputBridgeInternalLocked(token);
285 mProviderMap.remove(token);
286 }
287 }
288 }
289
290 @Override
291 public void clearInputBridge(TvRemoteProviderProxy provider, IBinder token) {
292 if (DEBUG) Slog.d(TAG, "clearInputBridge(), token: " + token);
293 synchronized (mLock) {
294 if (mProviderList.contains(provider)) {
295 mService.clearInputBridgeInternalLocked(token);
296 }
297 }
298 }
299
300 @Override
301 public void sendTimeStamp(TvRemoteProviderProxy provider, IBinder token, long timestamp) {
302 synchronized (mLock) {
303 if (mProviderList.contains(provider)) {
304 mService.sendTimeStampInternalLocked(token, timestamp);
305 }
306 }
307 }
308
309 @Override
310 public void sendKeyDown(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
311 if (DEBUG_KEYS) {
312 Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode);
313 }
314 synchronized (mLock) {
315 if (mProviderList.contains(provider)) {
316 mService.sendKeyDownInternalLocked(token, keyCode);
317 }
318 }
319 }
320
321 @Override
322 public void sendKeyUp(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
323 if (DEBUG_KEYS) {
324 Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode);
325 }
326 synchronized (mLock) {
327 if (mProviderList.contains(provider)) {
328 mService.sendKeyUpInternalLocked(token, keyCode);
329 }
330 }
331 }
332
333 @Override
334 public void sendPointerDown(TvRemoteProviderProxy provider, IBinder token, int pointerId,
335 int x, int y) {
336 if (DEBUG_KEYS) {
337 Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: " + pointerId);
338 }
339 synchronized (mLock) {
340 if (mProviderList.contains(provider)) {
341 mService.sendPointerDownInternalLocked(token, pointerId, x, y);
342 }
343 }
344 }
345
346 @Override
347 public void sendPointerUp(TvRemoteProviderProxy provider, IBinder token, int pointerId) {
348 if (DEBUG_KEYS) {
349 Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId);
350 }
351 synchronized (mLock) {
352 if (mProviderList.contains(provider)) {
353 mService.sendPointerUpInternalLocked(token, pointerId);
354 }
355 }
356 }
357
358 @Override
359 public void sendPointerSync(TvRemoteProviderProxy provider, IBinder token) {
360 if (DEBUG_KEYS) Slog.d(TAG, "sendPointerSync(), token: " + token);
361 synchronized (mLock) {
362 if (mProviderList.contains(provider)) {
363 mService.sendPointerSyncInternalLocked(token);
364 }
365 }
366 }
367
368 @Override
369 public void addProvider(TvRemoteProviderProxy provider) {
370 if (DEBUG) Slog.d(TAG, "addProvider " + provider);
371 synchronized (mLock) {
372 provider.setProviderSink(this);
373 mProviderList.add(provider);
374 Slog.d(TAG, "provider: " + provider.toString());
375 }
376 }
377
378 @Override
379 public void removeProvider(TvRemoteProviderProxy provider) {
380 if (DEBUG) Slog.d(TAG, "removeProvider " + provider);
381 synchronized (mLock) {
382 if (mProviderList.remove(provider) == false) {
383 Slog.e(TAG, "Unknown provider " + provider);
384 }
385 }
386 }
387 }
388}