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