• 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                 registerInputMethodMonitor();
60                 if (DEBUG) {
61                     Log.d(TAG, "Connecting to target: " + mTarget.getAddress());
62                 }
63                 // TODO need to start a timer, otherwise if the connection fails we might be
64                 // stuck here forever
65                 // must set CONNECTION_POLICY_ALLOWED or auto-connection will not
66                 // occur, however this setting does not appear to be sticky
67                 // across a reboot
68                 mInputProxy.setConnectionPolicy(mTarget, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
69             }
70         }
71     };
72 
73     private BluetoothHidHost mInputProxy;
74     private boolean mInputMethodMonitorRegistered = false;
75 
76     private BluetoothDevice mTarget;
77     private Context mContext;
78     private Handler mHandler;
79     private BluetoothDevicePairer.OpenConnectionCallback mOpenConnectionCallback;
80 
registerInputMethodMonitor()81     private void registerInputMethodMonitor() {
82         InputManager inputManager = (InputManager) mContext.getSystemService(Context.INPUT_SERVICE);
83         inputManager.registerInputDeviceListener(mInputListener, mHandler);
84 
85         // TO DO: The line below is a workaround for an issue in InputManager.
86         // The manager doesn't actually registers itself with the InputService
87         // unless we query it for input devices. We should remove this once
88         // the problem is fixed in InputManager.
89         // Reference bug in Frameworks: b/10415556
90         int[] inputDevices = inputManager.getInputDeviceIds();
91 
92         mInputMethodMonitorRegistered = true;
93     }
94 
95     private InputManager.InputDeviceListener mInputListener =
96             new InputManager.InputDeviceListener() {
97         @Override
98         public void onInputDeviceRemoved(int deviceId) {
99             // ignored
100         }
101 
102         @Override
103         public void onInputDeviceChanged(int deviceId) {
104             // ignored
105         }
106 
107         @Override
108         public void onInputDeviceAdded(int deviceId) {
109            if (BluetoothDevicePairer.hasValidInputDevice(mContext, new int[] {deviceId})) {
110                onInputAdded();
111            }
112         }
113     };
114 
onInputAdded()115     private void onInputAdded() {
116         unregisterInputMethodMonitor();
117         closeInputProfileProxy();
118         mOpenConnectionCallback.succeeded();
119     }
120 
unregisterInputMethodMonitor()121     private void unregisterInputMethodMonitor() {
122         if (mInputMethodMonitorRegistered) {
123             InputManager inputManager = (InputManager) mContext.getSystemService(Context.INPUT_SERVICE);
124             inputManager.unregisterInputDeviceListener(mInputListener);
125             mInputMethodMonitorRegistered = false;
126         }
127     }
128 
closeInputProfileProxy()129     private void closeInputProfileProxy() {
130         if (mInputProxy != null) {
131             try {
132                 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
133                 adapter.closeProfileProxy(BluetoothProfile.HID_HOST, mInputProxy);
134                 mInputProxy = null;
135             } catch (Throwable t) {
136                 Log.w(TAG, "Error cleaning up input profile proxy", t);
137             }
138         }
139     }
140 
BluetoothInputDeviceConnector()141     private BluetoothInputDeviceConnector() {
142     }
143 
BluetoothInputDeviceConnector(Context context, BluetoothDevice target, Handler handler, BluetoothDevicePairer.OpenConnectionCallback callback)144     public BluetoothInputDeviceConnector(Context context, BluetoothDevice target, Handler handler,
145                                          BluetoothDevicePairer.OpenConnectionCallback callback) {
146         mContext = context;
147         mTarget = target;
148         mHandler = handler;
149         mOpenConnectionCallback = callback;
150     }
151 
152     @Override
openConnection(BluetoothAdapter adapter)153     public void openConnection(BluetoothAdapter adapter) {
154         if (!adapter.getProfileProxy(mContext, mServiceConnection, BluetoothProfile.HID_HOST)) {
155             mOpenConnectionCallback.failed();
156         }
157     }
158 
159     @Override
dispose()160     public void dispose() {
161         unregisterInputMethodMonitor();
162         closeInputProfileProxy();
163     }
164 }
165