Home | History | Annotate | Download | only in tv
      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.server.tv;
     18 
     19 import android.content.Context;
     20 import android.os.Handler;
     21 import android.os.IBinder;
     22 import android.os.Looper;
     23 import android.os.Message;
     24 import android.util.ArrayMap;
     25 import android.util.Slog;
     26 
     27 import com.android.server.SystemService;
     28 import com.android.server.Watchdog;
     29 
     30 import java.io.IOException;
     31 import java.util.ArrayList;
     32 import 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  */
     41 public 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