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 17 package android.bluetooth; 18 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.content.Context; 22 import android.os.IBinder; 23 import android.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.util.Log; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 31 /** 32 * This class provides the public APIs to control the Bluetooth Input 33 * Device Profile. 34 * 35 *<p>BluetoothInputDevice is a proxy object for controlling the Bluetooth 36 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 37 * the BluetoothInputDevice proxy object. 38 * 39 *<p>Each method is protected with its appropriate permission. 40 *@hide 41 */ 42 public final class BluetoothInputDevice implements BluetoothProfile { 43 private static final String TAG = "BluetoothInputDevice"; 44 private static final boolean DBG = false; 45 46 /** 47 * Intent used to broadcast the change in connection state of the Input 48 * Device profile. 49 * 50 * <p>This intent will have 3 extras: 51 * <ul> 52 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 53 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> 54 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 55 * </ul> 56 * 57 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 58 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 59 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 60 * 61 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 62 * receive. 63 */ 64 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 65 public static final String ACTION_CONNECTION_STATE_CHANGED = 66 "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; 67 68 /** 69 * Return codes for the connect and disconnect Bluez / Dbus calls. 70 * @hide 71 */ 72 public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000; 73 74 /** 75 * @hide 76 */ 77 public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001; 78 79 /** 80 * @hide 81 */ 82 public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002; 83 84 /** 85 * @hide 86 */ 87 public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003; 88 89 /** 90 * @hide 91 */ 92 public static final int INPUT_OPERATION_SUCCESS = 5004; 93 94 private ServiceListener mServiceListener; 95 private BluetoothAdapter mAdapter; 96 private IBluetooth mService; 97 98 /** 99 * Create a BluetoothInputDevice proxy object for interacting with the local 100 * Bluetooth Service which handles the InputDevice profile 101 * 102 */ 103 /*package*/ BluetoothInputDevice(Context mContext, ServiceListener l) { 104 IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); 105 mServiceListener = l; 106 mAdapter = BluetoothAdapter.getDefaultAdapter(); 107 if (b != null) { 108 mService = IBluetooth.Stub.asInterface(b); 109 if (mServiceListener != null) { 110 mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, this); 111 } 112 } else { 113 Log.w(TAG, "Bluetooth Service not available!"); 114 115 // Instead of throwing an exception which prevents people from going 116 // into Wireless settings in the emulator. Let it crash later when it is actually used. 117 mService = null; 118 } 119 } 120 121 /*package*/ void close() { 122 mServiceListener = null; 123 } 124 125 /** 126 * Initiate connection to a profile of the remote bluetooth device. 127 * 128 * <p> The system supports connection to multiple input devices. 129 * 130 * <p> This API returns false in scenarios like the profile on the 131 * device is already connected or Bluetooth is not turned on. 132 * When this API returns true, it is guaranteed that 133 * connection state intent for the profile will be broadcasted with 134 * the state. Users can get the connection state of the profile 135 * from this intent. 136 * 137 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 138 * permission. 139 * 140 * @param device Remote Bluetooth Device 141 * @return false on immediate error, 142 * true otherwise 143 * @hide 144 */ 145 public boolean connect(BluetoothDevice device) { 146 if (DBG) log("connect(" + device + ")"); 147 if (mService != null && isEnabled() && 148 isValidDevice(device)) { 149 try { 150 return mService.connectInputDevice(device); 151 } catch (RemoteException e) { 152 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 153 return false; 154 } 155 } 156 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 157 return false; 158 } 159 160 /** 161 * Initiate disconnection from a profile 162 * 163 * <p> This API will return false in scenarios like the profile on the 164 * Bluetooth device is not in connected state etc. When this API returns, 165 * true, it is guaranteed that the connection state change 166 * intent will be broadcasted with the state. Users can get the 167 * disconnection state of the profile from this intent. 168 * 169 * <p> If the disconnection is initiated by a remote device, the state 170 * will transition from {@link #STATE_CONNECTED} to 171 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the 172 * host (local) device the state will transition from 173 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to 174 * state {@link #STATE_DISCONNECTED}. The transition to 175 * {@link #STATE_DISCONNECTING} can be used to distinguish between the 176 * two scenarios. 177 * 178 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 179 * permission. 180 * 181 * @param device Remote Bluetooth Device 182 * @return false on immediate error, 183 * true otherwise 184 * @hide 185 */ 186 public boolean disconnect(BluetoothDevice device) { 187 if (DBG) log("disconnect(" + device + ")"); 188 if (mService != null && isEnabled() && 189 isValidDevice(device)) { 190 try { 191 return mService.disconnectInputDevice(device); 192 } catch (RemoteException e) { 193 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 194 return false; 195 } 196 } 197 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 198 return false; 199 } 200 201 /** 202 * {@inheritDoc} 203 */ 204 public List<BluetoothDevice> getConnectedDevices() { 205 if (DBG) log("getConnectedDevices()"); 206 if (mService != null && isEnabled()) { 207 try { 208 return mService.getConnectedInputDevices(); 209 } catch (RemoteException e) { 210 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 211 return new ArrayList<BluetoothDevice>(); 212 } 213 } 214 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 215 return new ArrayList<BluetoothDevice>(); 216 } 217 218 /** 219 * {@inheritDoc} 220 */ 221 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 222 if (DBG) log("getDevicesMatchingStates()"); 223 if (mService != null && isEnabled()) { 224 try { 225 return mService.getInputDevicesMatchingConnectionStates(states); 226 } catch (RemoteException e) { 227 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 228 return new ArrayList<BluetoothDevice>(); 229 } 230 } 231 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 232 return new ArrayList<BluetoothDevice>(); 233 } 234 235 /** 236 * {@inheritDoc} 237 */ 238 public int getConnectionState(BluetoothDevice device) { 239 if (DBG) log("getState(" + device + ")"); 240 if (mService != null && isEnabled() 241 && isValidDevice(device)) { 242 try { 243 return mService.getInputDeviceConnectionState(device); 244 } catch (RemoteException e) { 245 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 246 return BluetoothProfile.STATE_DISCONNECTED; 247 } 248 } 249 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 250 return BluetoothProfile.STATE_DISCONNECTED; 251 } 252 253 /** 254 * Set priority of the profile 255 * 256 * <p> The device should already be paired. 257 * Priority can be one of {@link #PRIORITY_ON} or 258 * {@link #PRIORITY_OFF}, 259 * 260 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 261 * permission. 262 * 263 * @param device Paired bluetooth device 264 * @param priority 265 * @return true if priority is set, false on error 266 * @hide 267 */ 268 public boolean setPriority(BluetoothDevice device, int priority) { 269 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 270 if (mService != null && isEnabled() 271 && isValidDevice(device)) { 272 if (priority != BluetoothProfile.PRIORITY_OFF && 273 priority != BluetoothProfile.PRIORITY_ON) { 274 return false; 275 } 276 try { 277 return mService.setInputDevicePriority(device, priority); 278 } catch (RemoteException e) { 279 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 280 return false; 281 } 282 } 283 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 284 return false; 285 } 286 287 /** 288 * Get the priority of the profile. 289 * 290 * <p> The priority can be any of: 291 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, 292 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} 293 * 294 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 295 * 296 * @param device Bluetooth device 297 * @return priority of the device 298 * @hide 299 */ 300 public int getPriority(BluetoothDevice device) { 301 if (DBG) log("getPriority(" + device + ")"); 302 if (mService != null && isEnabled() 303 && isValidDevice(device)) { 304 try { 305 return mService.getInputDevicePriority(device); 306 } catch (RemoteException e) { 307 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 308 return BluetoothProfile.PRIORITY_OFF; 309 } 310 } 311 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 312 return BluetoothProfile.PRIORITY_OFF; 313 } 314 315 private boolean isEnabled() { 316 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; 317 return false; 318 } 319 320 private boolean isValidDevice(BluetoothDevice device) { 321 if (device == null) return false; 322 323 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; 324 return false; 325 } 326 327 private static void log(String msg) { 328 Log.d(TAG, msg); 329 } 330 } 331