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