TvRemoteService.java revision b5b86c11008422ac4bf5af5fed736f04ebbaa858
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.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}
389