1/* 2 * Copyright (C) 2011 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 17package android.server; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothDevice; 21import android.bluetooth.BluetoothDeviceProfileState; 22import android.bluetooth.BluetoothInputDevice; 23import android.bluetooth.BluetoothProfile; 24import android.bluetooth.BluetoothProfileState; 25import android.content.Context; 26import android.content.Intent; 27import android.os.Message; 28import android.provider.Settings; 29import android.util.Log; 30 31import java.util.ArrayList; 32import java.util.HashMap; 33import java.util.List; 34 35/** 36 * This handles all the operations on the HID profile. 37 * All functions are called by BluetoothService, as Bluetooth Service 38 * is the Service handler for the HID profile. 39 */ 40final class BluetoothInputProfileHandler { 41 private static final String TAG = "BluetoothInputProfileHandler"; 42 private static final boolean DBG = true; 43 44 public static BluetoothInputProfileHandler sInstance; 45 private Context mContext; 46 private BluetoothService mBluetoothService; 47 private final HashMap<BluetoothDevice, Integer> mInputDevices; 48 private final BluetoothProfileState mHidProfileState; 49 50 private BluetoothInputProfileHandler(Context context, BluetoothService service) { 51 mContext = context; 52 mBluetoothService = service; 53 mInputDevices = new HashMap<BluetoothDevice, Integer>(); 54 mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID); 55 mHidProfileState.start(); 56 } 57 58 static synchronized BluetoothInputProfileHandler getInstance(Context context, 59 BluetoothService service) { 60 if (sInstance == null) sInstance = new BluetoothInputProfileHandler(context, service); 61 return sInstance; 62 } 63 64 boolean connectInputDevice(BluetoothDevice device, 65 BluetoothDeviceProfileState state) { 66 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); 67 if (objectPath == null || 68 getInputDeviceConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED || 69 getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) { 70 return false; 71 } 72 if (state != null) { 73 Message msg = new Message(); 74 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING; 75 msg.obj = state; 76 mHidProfileState.sendMessage(msg); 77 return true; 78 } 79 return false; 80 } 81 82 boolean connectInputDeviceInternal(BluetoothDevice device) { 83 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); 84 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); 85 if (!mBluetoothService.connectInputDeviceNative(objectPath)) { 86 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED); 87 return false; 88 } 89 return true; 90 } 91 92 boolean disconnectInputDevice(BluetoothDevice device, 93 BluetoothDeviceProfileState state) { 94 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); 95 if (objectPath == null || 96 getInputDeviceConnectionState(device) == BluetoothInputDevice.STATE_DISCONNECTED) { 97 return false; 98 } 99 if (state != null) { 100 Message msg = new Message(); 101 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING; 102 msg.obj = state; 103 mHidProfileState.sendMessage(msg); 104 return true; 105 } 106 return false; 107 } 108 109 boolean disconnectInputDeviceInternal(BluetoothDevice device) { 110 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); 111 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); 112 if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) { 113 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED); 114 return false; 115 } 116 return true; 117 } 118 119 int getInputDeviceConnectionState(BluetoothDevice device) { 120 if (mInputDevices.get(device) == null) { 121 return BluetoothInputDevice.STATE_DISCONNECTED; 122 } 123 return mInputDevices.get(device); 124 } 125 126 List<BluetoothDevice> getConnectedInputDevices() { 127 List<BluetoothDevice> devices = lookupInputDevicesMatchingStates( 128 new int[] {BluetoothInputDevice.STATE_CONNECTED}); 129 return devices; 130 } 131 132 List<BluetoothDevice> getInputDevicesMatchingConnectionStates(int[] states) { 133 List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(states); 134 return devices; 135 } 136 137 int getInputDevicePriority(BluetoothDevice device) { 138 return Settings.Secure.getInt(mContext.getContentResolver(), 139 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), 140 BluetoothInputDevice.PRIORITY_UNDEFINED); 141 } 142 143 boolean setInputDevicePriority(BluetoothDevice device, int priority) { 144 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { 145 return false; 146 } 147 return Settings.Secure.putInt(mContext.getContentResolver(), 148 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), 149 priority); 150 } 151 152 List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) { 153 List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>(); 154 155 for (BluetoothDevice device: mInputDevices.keySet()) { 156 int inputDeviceState = getInputDeviceConnectionState(device); 157 for (int state : states) { 158 if (state == inputDeviceState) { 159 inputDevices.add(device); 160 break; 161 } 162 } 163 } 164 return inputDevices; 165 } 166 167 private void handleInputDeviceStateChange(BluetoothDevice device, int state) { 168 int prevState; 169 if (mInputDevices.get(device) == null) { 170 prevState = BluetoothInputDevice.STATE_DISCONNECTED; 171 } else { 172 prevState = mInputDevices.get(device); 173 } 174 if (prevState == state) return; 175 176 mInputDevices.put(device, state); 177 178 if (getInputDevicePriority(device) > 179 BluetoothInputDevice.PRIORITY_OFF && 180 state == BluetoothInputDevice.STATE_CONNECTING || 181 state == BluetoothInputDevice.STATE_CONNECTED) { 182 // We have connected or attempting to connect. 183 // Bump priority 184 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT); 185 } 186 187 Intent intent = new Intent(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED); 188 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 189 intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_STATE, prevState); 190 intent.putExtra(BluetoothInputDevice.EXTRA_STATE, state); 191 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 192 mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); 193 194 debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state); 195 mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.INPUT_DEVICE, state, 196 prevState); 197 } 198 199 void handleInputDevicePropertyChange(String address, boolean connected) { 200 int state = connected ? BluetoothInputDevice.STATE_CONNECTED : 201 BluetoothInputDevice.STATE_DISCONNECTED; 202 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 203 BluetoothDevice device = adapter.getRemoteDevice(address); 204 handleInputDeviceStateChange(device, state); 205 } 206 207 void setInitialInputDevicePriority(BluetoothDevice device, int state) { 208 switch (state) { 209 case BluetoothDevice.BOND_BONDED: 210 if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) { 211 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON); 212 } 213 break; 214 case BluetoothDevice.BOND_NONE: 215 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED); 216 break; 217 } 218 } 219 220 private static void debugLog(String msg) { 221 if (DBG) Log.d(TAG, msg); 222 } 223 224 private static void errorLog(String msg) { 225 Log.e(TAG, msg); 226 } 227} 228