HeadsetService.java revision 74ae04c73312403e89db0f8e9bd9601d403b4783
1/* 2 * Copyright (C) 2012 Google Inc. 3 */ 4 5package com.android.bluetooth.hfp; 6 7import android.bluetooth.BluetoothDevice; 8import android.bluetooth.BluetoothHeadset; 9import android.bluetooth.BluetoothProfile; 10import android.bluetooth.IBluetoothHeadset; 11import android.content.BroadcastReceiver; 12import android.content.Context; 13import android.content.Intent; 14import android.content.IntentFilter; 15import android.media.AudioManager; 16import android.os.Handler; 17import android.os.Message; 18import android.provider.Settings; 19import android.util.Log; 20 21import java.util.ArrayList; 22import java.util.List; 23import java.util.Iterator; 24import java.util.Map; 25import android.content.pm.PackageManager; 26import com.android.bluetooth.btservice.ProfileService; 27 28/** 29 * Provides Bluetooth Headset and Handsfree profile, as a service in 30 * the Bluetooth application. 31 * @hide 32 */ 33public class HeadsetService extends ProfileService { 34 private static final boolean DBG = true; 35 private static final String TAG = "HeadsetService"; 36 private HeadsetStateMachine mStateMachine; 37 38 protected String getName() { 39 return TAG; 40 } 41 42 public IProfileServiceBinder initBinder() { 43 return new BluetoothHeadsetBinder(this); 44 } 45 46 protected boolean start() { 47 mStateMachine = new HeadsetStateMachine(this); 48 mStateMachine.start(); 49 IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 50 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); 51 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 52 try { 53 registerReceiver(mHeadsetReceiver, filter); 54 } catch (Exception e) { 55 Log.w(TAG,"Unable to register headset receiver",e); 56 } 57 return true; 58 } 59 60 protected boolean stop() { 61 try { 62 unregisterReceiver(mHeadsetReceiver); 63 } catch (Exception e) { 64 Log.w(TAG,"Unable to unregister headset receiver",e); 65 } 66 mStateMachine.quit(); 67 return true; 68 } 69 70 protected boolean cleanup() { 71 if (mStateMachine != null) { 72 mStateMachine.cleanup(); 73 mStateMachine=null; 74 } 75 return true; 76 } 77 78 private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() { 79 @Override 80 public void onReceive(Context context, Intent intent) { 81 String action = intent.getAction(); 82 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 83 mStateMachine.sendMessage(HeadsetStateMachine.INTENT_BATTERY_CHANGED, intent); 84 } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { 85 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 86 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { 87 mStateMachine.sendMessage(HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, 88 intent); 89 } 90 } 91 else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) { 92 Log.v(TAG, "HeadsetService - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY"); 93 mStateMachine.handleAccessPermissionResult(intent); 94 } 95 } 96 }; 97 98 /** 99 * Handlers for incoming service calls 100 */ 101 private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub implements IProfileServiceBinder { 102 private HeadsetService mService; 103 104 public BluetoothHeadsetBinder(HeadsetService svc) { 105 mService = svc; 106 } 107 public boolean cleanup() { 108 mService = null; 109 return true; 110 } 111 112 private HeadsetService getService() { 113 if (mService != null && mService.isAvailable()) { 114 return mService; 115 } 116 return null; 117 } 118 119 public boolean connect(BluetoothDevice device) { 120 HeadsetService service = getService(); 121 if (service == null) return false; 122 return service.connect(device); 123 } 124 125 public boolean disconnect(BluetoothDevice device) { 126 HeadsetService service = getService(); 127 if (service == null) return false; 128 return service.disconnect(device); 129 } 130 131 public List<BluetoothDevice> getConnectedDevices() { 132 HeadsetService service = getService(); 133 if (service == null) return new ArrayList<BluetoothDevice>(0); 134 return service.getConnectedDevices(); 135 } 136 137 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 138 HeadsetService service = getService(); 139 if (service == null) return new ArrayList<BluetoothDevice>(0); 140 return service.getDevicesMatchingConnectionStates(states); 141 } 142 143 public int getConnectionState(BluetoothDevice device) { 144 HeadsetService service = getService(); 145 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 146 return service.getConnectionState(device); 147 } 148 149 public boolean setPriority(BluetoothDevice device, int priority) { 150 HeadsetService service = getService(); 151 if (service == null) return false; 152 return service.setPriority(device, priority); 153 } 154 155 public int getPriority(BluetoothDevice device) { 156 HeadsetService service = getService(); 157 if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; 158 return service.getPriority(device); 159 } 160 161 public boolean startVoiceRecognition(BluetoothDevice device) { 162 HeadsetService service = getService(); 163 if (service == null) return false; 164 return service.startVoiceRecognition(device); 165 } 166 167 public boolean stopVoiceRecognition(BluetoothDevice device) { 168 HeadsetService service = getService(); 169 if (service == null) return false; 170 return service.stopVoiceRecognition(device); 171 } 172 173 public boolean isAudioOn() { 174 HeadsetService service = getService(); 175 if (service == null) return false; 176 return service.isAudioOn(); 177 } 178 179 public boolean isAudioConnected(BluetoothDevice device) { 180 HeadsetService service = getService(); 181 if (service == null) return false; 182 return service.isAudioConnected(device); 183 } 184 185 public int getBatteryUsageHint(BluetoothDevice device) { 186 HeadsetService service = getService(); 187 if (service == null) return 0; 188 return service.getBatteryUsageHint(device); 189 } 190 191 public boolean acceptIncomingConnect(BluetoothDevice device) { 192 HeadsetService service = getService(); 193 if (service == null) return false; 194 return service.acceptIncomingConnect(device); 195 } 196 197 public boolean rejectIncomingConnect(BluetoothDevice device) { 198 HeadsetService service = getService(); 199 if (service == null) return false; 200 return service.rejectIncomingConnect(device); 201 } 202 203 public int getAudioState(BluetoothDevice device) { 204 HeadsetService service = getService(); 205 if (service == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 206 return service.getAudioState(device); 207 } 208 209 public boolean connectAudio() { 210 HeadsetService service = getService(); 211 if (service == null) return false; 212 return service.connectAudio(); 213 } 214 215 public boolean disconnectAudio() { 216 HeadsetService service = getService(); 217 if (service == null) return false; 218 return service.disconnectAudio(); 219 } 220 221 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 222 HeadsetService service = getService(); 223 if (service == null) return false; 224 return service.startScoUsingVirtualVoiceCall(device); 225 } 226 227 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 228 HeadsetService service = getService(); 229 if (service == null) return false; 230 return service.stopScoUsingVirtualVoiceCall(device); 231 } 232 233 public void phoneStateChanged(int numActive, int numHeld, int callState, 234 String number, int type) { 235 HeadsetService service = getService(); 236 if (service == null) return; 237 service.phoneStateChanged(numActive, numHeld, callState, number, type); 238 } 239 240 public void roamChanged(boolean roam) { 241 HeadsetService service = getService(); 242 if (service == null) return; 243 service.roamChanged(roam); 244 } 245 246 public void clccResponse(int index, int direction, int status, int mode, boolean mpty, 247 String number, int type) { 248 HeadsetService service = getService(); 249 if (service == null) return; 250 service.clccResponse(index, direction, status, mode, mpty, number, type); 251 } 252 }; 253 254 //API methods 255 boolean connect(BluetoothDevice device) { 256 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 257 "Need BLUETOOTH ADMIN permission"); 258 259 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 260 return false; 261 } 262 263 int connectionState = mStateMachine.getConnectionState(device); 264 if (connectionState == BluetoothProfile.STATE_CONNECTED || 265 connectionState == BluetoothProfile.STATE_CONNECTING) { 266 return false; 267 } 268 269 mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); 270 return true; 271 } 272 273 boolean disconnect(BluetoothDevice device) { 274 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 275 "Need BLUETOOTH ADMIN permission"); 276 int connectionState = mStateMachine.getConnectionState(device); 277 if (connectionState != BluetoothProfile.STATE_CONNECTED && 278 connectionState != BluetoothProfile.STATE_CONNECTING) { 279 return false; 280 } 281 282 mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); 283 return true; 284 } 285 286 List<BluetoothDevice> getConnectedDevices() { 287 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 288 return mStateMachine.getConnectedDevices(); 289 } 290 291 private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 292 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 293 return mStateMachine.getDevicesMatchingConnectionStates(states); 294 } 295 296 int getConnectionState(BluetoothDevice device) { 297 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 298 return mStateMachine.getConnectionState(device); 299 } 300 301 boolean setPriority(BluetoothDevice device, int priority) { 302 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 303 "Need BLUETOOTH_ADMIN permission"); 304 Settings.Secure.putInt(getContentResolver(), 305 Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), 306 priority); 307 if (DBG) Log.d(TAG, "Saved priority " + device + " = " + priority); 308 return true; 309 } 310 311 int getPriority(BluetoothDevice device) { 312 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 313 "Need BLUETOOTH_ADMIN permission"); 314 int priority = Settings.Secure.getInt(getContentResolver(), 315 Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), 316 BluetoothProfile.PRIORITY_UNDEFINED); 317 return priority; 318 } 319 320 boolean startVoiceRecognition(BluetoothDevice device) { 321 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 322 int connectionState = mStateMachine.getConnectionState(device); 323 if (connectionState != BluetoothProfile.STATE_CONNECTED && 324 connectionState != BluetoothProfile.STATE_CONNECTING) { 325 return false; 326 } 327 mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START); 328 return true; 329 } 330 331 boolean stopVoiceRecognition(BluetoothDevice device) { 332 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 333 // It seem that we really need to check the AudioOn state. 334 // But since we allow startVoiceRecognition in STATE_CONNECTED and 335 // STATE_CONNECTING state, we do these 2 in this method 336 int connectionState = mStateMachine.getConnectionState(device); 337 if (connectionState != BluetoothProfile.STATE_CONNECTED && 338 connectionState != BluetoothProfile.STATE_CONNECTING) { 339 return false; 340 } 341 mStateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP); 342 // TODO is this return correct when the voice recognition is not on? 343 return true; 344 } 345 346 boolean isAudioOn() { 347 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 348 return mStateMachine.isAudioOn(); 349 } 350 351 boolean isAudioConnected(BluetoothDevice device) { 352 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 353 return mStateMachine.isAudioConnected(device); 354 } 355 356 int getBatteryUsageHint(BluetoothDevice device) { 357 // TODO(BT) ask for BT stack support? 358 return 0; 359 } 360 361 boolean acceptIncomingConnect(BluetoothDevice device) { 362 // TODO(BT) remove it if stack does access control 363 return false; 364 } 365 366 boolean rejectIncomingConnect(BluetoothDevice device) { 367 // TODO(BT) remove it if stack does access control 368 return false; 369 } 370 371 int getAudioState(BluetoothDevice device) { 372 return mStateMachine.getAudioState(device); 373 } 374 375 boolean connectAudio() { 376 // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission 377 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 378 if (!mStateMachine.isConnected()) { 379 return false; 380 } 381 if (mStateMachine.isAudioOn()) { 382 return false; 383 } 384 mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO); 385 return true; 386 } 387 388 boolean disconnectAudio() { 389 // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission 390 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 391 if (!mStateMachine.isAudioOn()) { 392 return false; 393 } 394 mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO); 395 return true; 396 } 397 398 boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 399 // TODO(BT) Is this right? 400 mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 401 return true; 402 } 403 404 boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 405 // TODO(BT) Is this right? 406 mStateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 407 return true; 408 } 409 410 private void phoneStateChanged(int numActive, int numHeld, int callState, 411 String number, int type) { 412 mStateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED, 413 new HeadsetCallState(numActive, numHeld, callState, number, type)); 414 } 415 416 private void roamChanged(boolean roam) { 417 mStateMachine.sendMessage(HeadsetStateMachine.ROAM_CHANGED, roam); 418 } 419 420 private void clccResponse(int index, int direction, int status, int mode, boolean mpty, 421 String number, int type) { 422 mStateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE, 423 new HeadsetClccResponse(index, direction, status, mode, mpty, number, type)); 424 } 425 426} 427