Home | History | Annotate | Download | only in accessories
      1 /*
      2  * Copyright (C) 2014 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.tv.settings.accessories;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.bluetooth.BluetoothInputDevice;
     22 import android.bluetooth.BluetoothProfile;
     23 import android.content.Context;
     24 import android.hardware.input.InputManager;
     25 import android.os.Handler;
     26 import android.util.Log;
     27 import android.view.InputDevice;
     28 
     29 /**
     30  * Manages process of pairing and connecting of input devices.
     31  */
     32 public class BluetoothInputDeviceConnector implements BluetoothDevicePairer.BluetoothConnector {
     33 
     34     public static final String TAG = "BtInputDeviceConnector";
     35 
     36     private static final boolean DEBUG = false;
     37 
     38     private static final String[] INVALID_INPUT_KEYBOARD_DEVICE_NAMES = {
     39         "gpio-keypad", "cec_keyboard", "Virtual", "athome_remote"
     40     };
     41 
     42     private BluetoothProfile.ServiceListener mServiceConnection =
     43             new BluetoothProfile.ServiceListener() {
     44 
     45         @Override
     46         public void onServiceDisconnected(int profile) {
     47             Log.w(TAG, "Service disconnected, perhaps unexpectedly");
     48             unregisterInputMethodMonitor();
     49             closeInputProfileProxy();
     50             mOpenConnectionCallback.failed();
     51         }
     52 
     53         @Override
     54         public void onServiceConnected(int profile, BluetoothProfile proxy) {
     55             if (DEBUG) {
     56                 Log.d(TAG, "Connection made to bluetooth proxy.");
     57             }
     58             mInputProxy = (BluetoothInputDevice) proxy;
     59             if (mTarget != null) {
     60                 registerInputMethodMonitor();
     61                 if (DEBUG) {
     62                     Log.d(TAG, "Connecting to target: " + mTarget.getAddress());
     63                 }
     64                 // TODO need to start a timer, otherwise if the connection fails we might be
     65                 // stuck here forever
     66                 mInputProxy.connect(mTarget);
     67 
     68                 // must set PRIORITY_AUTO_CONNECT or auto-connection will not
     69                 // occur, however this setting does not appear to be sticky
     70                 // across a reboot
     71                 mInputProxy.setPriority(mTarget, BluetoothProfile.PRIORITY_AUTO_CONNECT);
     72             }
     73         }
     74     };
     75 
     76     private BluetoothInputDevice mInputProxy;
     77     private boolean mInputMethodMonitorRegistered = false;
     78 
     79     private BluetoothDevice mTarget;
     80     private Context mContext;
     81     private Handler mHandler;
     82     private BluetoothDevicePairer.OpenConnectionCallback mOpenConnectionCallback;
     83 
     84     private void registerInputMethodMonitor() {
     85         InputManager inputManager = (InputManager) mContext.getSystemService(Context.INPUT_SERVICE);
     86         inputManager.registerInputDeviceListener(mInputListener, mHandler);
     87 
     88         // TO DO: The line below is a workaround for an issue in InputManager.
     89         // The manager doesn't actually registers itself with the InputService
     90         // unless we query it for input devices. We should remove this once
     91         // the problem is fixed in InputManager.
     92         // Reference bug in Frameworks: b/10415556
     93         int[] inputDevices = inputManager.getInputDeviceIds();
     94 
     95         mInputMethodMonitorRegistered = true;
     96     }
     97 
     98     private InputManager.InputDeviceListener mInputListener =
     99             new InputManager.InputDeviceListener() {
    100         @Override
    101         public void onInputDeviceRemoved(int deviceId) {
    102             // ignored
    103         }
    104 
    105         @Override
    106         public void onInputDeviceChanged(int deviceId) {
    107             // ignored
    108         }
    109 
    110         @Override
    111         public void onInputDeviceAdded(int deviceId) {
    112            if (BluetoothDevicePairer.hasValidInputDevice(mContext, new int[] {deviceId})) {
    113                onInputAdded();
    114            }
    115         }
    116     };
    117 
    118     private void onInputAdded() {
    119         unregisterInputMethodMonitor();
    120         closeInputProfileProxy();
    121         mOpenConnectionCallback.succeeded();
    122     }
    123 
    124     private void unregisterInputMethodMonitor() {
    125         if (mInputMethodMonitorRegistered) {
    126             InputManager inputManager = (InputManager) mContext.getSystemService(Context.INPUT_SERVICE);
    127             inputManager.unregisterInputDeviceListener(mInputListener);
    128             mInputMethodMonitorRegistered = false;
    129         }
    130     }
    131 
    132     private void closeInputProfileProxy() {
    133         if (mInputProxy != null) {
    134             try {
    135                 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    136                 adapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mInputProxy);
    137                 mInputProxy = null;
    138             } catch (Throwable t) {
    139                 Log.w(TAG, "Error cleaning up input profile proxy", t);
    140             }
    141         }
    142     }
    143 
    144     private BluetoothInputDeviceConnector() {
    145     }
    146 
    147     public BluetoothInputDeviceConnector(Context context, BluetoothDevice target, Handler handler,
    148                                          BluetoothDevicePairer.OpenConnectionCallback callback) {
    149         mContext = context;
    150         mTarget = target;
    151         mHandler = handler;
    152         mOpenConnectionCallback = callback;
    153     }
    154 
    155     @Override
    156     public void openConnection(BluetoothAdapter adapter) {
    157         if (!adapter.getProfileProxy(mContext, mServiceConnection, BluetoothProfile.INPUT_DEVICE)) {
    158             mOpenConnectionCallback.failed();
    159         }
    160     }
    161 }
    162