blob: 961c9925414abc5313bdd2151dedfaa360746bdd [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.tv;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.server.SystemService;
import com.android.server.Watchdog;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
/**
* TvRemoteService represents a system service that allows a connected
* remote control (emote) service to inject white-listed input events
* and call other specified methods for functioning as an emote service.
* <p/>
* This service is intended for use only by white-listed packages.
*/
public class TvRemoteService extends SystemService implements Watchdog.Monitor {
private static final String TAG = "TvRemoteService";
private static final boolean DEBUG = false;
private static final boolean DEBUG_KEYS = false;
private Map<IBinder, UinputBridge> mBridgeMap = new ArrayMap();
private Map<IBinder, TvRemoteProviderProxy> mProviderMap = new ArrayMap();
private ArrayList<TvRemoteProviderProxy> mProviderList = new ArrayList<>();
/**
* State guarded by mLock.
* This is the second lock in sequence for an incoming call.
* The first lock is always {@link TvRemoteProviderProxy#mLock}
*
* There are currently no methods that break this sequence.
* Special note:
* Outgoing call informInputBridgeConnected(), which is called from
* openInputBridgeInternalLocked() uses a handler thereby relinquishing held locks.
*/
private final Object mLock = new Object();
public final UserHandler mHandler;
public TvRemoteService(Context context) {
super(context);
mHandler = new UserHandler(new UserProvider(TvRemoteService.this), context);
Watchdog.getInstance().addMonitor(this);
}
@Override
public void onStart() {
if (DEBUG) Slog.d(TAG, "onStart()");
}
@Override
public void monitor() {
synchronized (mLock) { /* check for deadlock */ }
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
if (DEBUG) Slog.d(TAG, "PHASE_THIRD_PARTY_APPS_CAN_START");
mHandler.sendEmptyMessage(UserHandler.MSG_START);
}
}
//Outgoing calls.
private void informInputBridgeConnected(IBinder token) {
mHandler.obtainMessage(UserHandler.MSG_INPUT_BRIDGE_CONNECTED, 0, 0, token).sendToTarget();
}
// Incoming calls.
private void openInputBridgeInternalLocked(TvRemoteProviderProxy provider, IBinder token,
String name, int width, int height,
int maxPointers) {
if (DEBUG) {
Slog.d(TAG, "openInputBridgeInternalLocked(), token: " + token + ", name: " + name +
", width: " + width + ", height: " + height + ", maxPointers: " + maxPointers);
}
try {
//Create a new bridge, if one does not exist already
if (mBridgeMap.containsKey(token)) {
if (DEBUG) Slog.d(TAG, "RemoteBridge already exists");
// Respond back with success.
informInputBridgeConnected(token);
return;
}
UinputBridge inputBridge = new UinputBridge(token, name, width, height, maxPointers);
mBridgeMap.put(token, inputBridge);
mProviderMap.put(token, provider);
// Respond back with success.
informInputBridgeConnected(token);
} catch (IOException ioe) {
Slog.e(TAG, "Cannot create device for " + name);
}
}
private void closeInputBridgeInternalLocked(IBinder token) {
if (DEBUG) {
Slog.d(TAG, "closeInputBridgeInternalLocked(), token: " + token);
}
// Close an existing RemoteBridge
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.close(token);
}
mBridgeMap.remove(token);
}
private void clearInputBridgeInternalLocked(IBinder token) {
if (DEBUG) {
Slog.d(TAG, "clearInputBridgeInternalLocked(), token: " + token);
}
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.clear(token);
}
}
private void sendTimeStampInternalLocked(IBinder token, long timestamp) {
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.sendTimestamp(token, timestamp);
}
}
private void sendKeyDownInternalLocked(IBinder token, int keyCode) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendKeyDownInternalLocked(), token: " + token + ", keyCode: " + keyCode);
}
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.sendKeyDown(token, keyCode);
}
}
private void sendKeyUpInternalLocked(IBinder token, int keyCode) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendKeyUpInternalLocked(), token: " + token + ", keyCode: " + keyCode);
}
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.sendKeyUp(token, keyCode);
}
}
private void sendPointerDownInternalLocked(IBinder token, int pointerId, int x, int y) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendPointerDownInternalLocked(), token: " + token + ", pointerId: " +
pointerId + ", x: " + x + ", y: " + y);
}
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.sendPointerDown(token, pointerId, x, y);
}
}
private void sendPointerUpInternalLocked(IBinder token, int pointerId) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendPointerUpInternalLocked(), token: " + token + ", pointerId: " +
pointerId);
}
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.sendPointerUp(token, pointerId);
}
}
private void sendPointerSyncInternalLocked(IBinder token) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendPointerSyncInternalLocked(), token: " + token);
}
UinputBridge inputBridge = mBridgeMap.get(token);
if (inputBridge != null) {
inputBridge.sendPointerSync(token);
}
}
private final class UserHandler extends Handler {
public static final int MSG_START = 1;
public static final int MSG_INPUT_BRIDGE_CONNECTED = 2;
private final TvRemoteProviderWatcher mWatcher;
private boolean mRunning;
public UserHandler(UserProvider provider, Context context) {
super(Looper.getMainLooper(), null, true);
mWatcher = new TvRemoteProviderWatcher(context, provider, this);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START: {
start();
break;
}
case MSG_INPUT_BRIDGE_CONNECTED: {
IBinder token = (IBinder) msg.obj;
TvRemoteProviderProxy provider = mProviderMap.get(token);
if (provider != null) {
provider.inputBridgeConnected(token);
}
break;
}
}
}
private void start() {
if (!mRunning) {
mRunning = true;
mWatcher.start(); // also starts all providers
}
}
}
private final class UserProvider implements TvRemoteProviderWatcher.ProviderMethods,
TvRemoteProviderProxy.ProviderMethods {
private final TvRemoteService mService;
public UserProvider(TvRemoteService service) {
mService = service;
}
@Override
public void openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
int width, int height, int maxPointers) {
if (DEBUG) {
Slog.d(TAG, "openInputBridge(), token: " + token +
", name: " + name + ", width: " + width +
", height: " + height + ", maxPointers: " + maxPointers);
}
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.openInputBridgeInternalLocked(provider, token, name, width, height,
maxPointers);
}
}
}
@Override
public void closeInputBridge(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG) Slog.d(TAG, "closeInputBridge(), token: " + token);
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.closeInputBridgeInternalLocked(token);
mProviderMap.remove(token);
}
}
}
@Override
public void clearInputBridge(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG) Slog.d(TAG, "clearInputBridge(), token: " + token);
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.clearInputBridgeInternalLocked(token);
}
}
}
@Override
public void sendTimeStamp(TvRemoteProviderProxy provider, IBinder token, long timestamp) {
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.sendTimeStampInternalLocked(token, timestamp);
}
}
}
@Override
public void sendKeyDown(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode);
}
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.sendKeyDownInternalLocked(token, keyCode);
}
}
}
@Override
public void sendKeyUp(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode);
}
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.sendKeyUpInternalLocked(token, keyCode);
}
}
}
@Override
public void sendPointerDown(TvRemoteProviderProxy provider, IBinder token, int pointerId,
int x, int y) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: " + pointerId);
}
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.sendPointerDownInternalLocked(token, pointerId, x, y);
}
}
}
@Override
public void sendPointerUp(TvRemoteProviderProxy provider, IBinder token, int pointerId) {
if (DEBUG_KEYS) {
Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId);
}
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.sendPointerUpInternalLocked(token, pointerId);
}
}
}
@Override
public void sendPointerSync(TvRemoteProviderProxy provider, IBinder token) {
if (DEBUG_KEYS) Slog.d(TAG, "sendPointerSync(), token: " + token);
synchronized (mLock) {
if (mProviderList.contains(provider)) {
mService.sendPointerSyncInternalLocked(token);
}
}
}
@Override
public void addProvider(TvRemoteProviderProxy provider) {
if (DEBUG) Slog.d(TAG, "addProvider " + provider);
synchronized (mLock) {
provider.setProviderSink(this);
mProviderList.add(provider);
Slog.d(TAG, "provider: " + provider.toString());
}
}
@Override
public void removeProvider(TvRemoteProviderProxy provider) {
if (DEBUG) Slog.d(TAG, "removeProvider " + provider);
synchronized (mLock) {
if (mProviderList.remove(provider) == false) {
Slog.e(TAG, "Unknown provider " + provider);
}
}
}
}
}