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