A2dpService.java revision 1a5532f2043cc2eaf136a35eace240f8bebd9bbe
1/* 2 * Copyright (C) 2012 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 com.android.bluetooth.a2dp; 18 19import android.bluetooth.BluetoothA2dp; 20import android.bluetooth.BluetoothCodecConfig; 21import android.bluetooth.BluetoothCodecStatus; 22import android.bluetooth.BluetoothDevice; 23import android.bluetooth.BluetoothProfile; 24import android.bluetooth.BluetoothUuid; 25import android.bluetooth.IBluetoothA2dp; 26import android.content.BroadcastReceiver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.os.HandlerThread; 31import android.os.ParcelUuid; 32import android.provider.Settings; 33import android.util.Log; 34 35import com.android.bluetooth.Utils; 36import com.android.bluetooth.avrcp.Avrcp; 37import com.android.bluetooth.btservice.ProfileService; 38 39import java.util.ArrayList; 40import java.util.List; 41import java.util.Objects; 42 43/** 44 * Provides Bluetooth A2DP profile, as a service in the Bluetooth application. 45 * @hide 46 */ 47public class A2dpService extends ProfileService { 48 private static final boolean DBG = true; 49 private static final String TAG = "A2dpService"; 50 51 private HandlerThread mStateMachinesThread = null; 52 private A2dpStateMachine mStateMachine; 53 private Avrcp mAvrcp; 54 private A2dpNativeInterface mA2dpNativeInterface = null; 55 56 private BroadcastReceiver mConnectionStateChangedReceiver = null; 57 58 public A2dpService() { 59 mA2dpNativeInterface = A2dpNativeInterface.getInstance(); 60 } 61 62 private class CodecSupportReceiver extends BroadcastReceiver { 63 @Override 64 public void onReceive(Context context, Intent intent) { 65 if (!BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { 66 return; 67 } 68 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 69 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 70 if (state != BluetoothProfile.STATE_CONNECTED || device == null) { 71 return; 72 } 73 // Each time a device connects, we want to re-check if it supports optional 74 // codecs (perhaps it's had a firmware update, etc.) and save that state if 75 // it differs from what we had saved before. 76 int previousSupport = getSupportsOptionalCodecs(device); 77 boolean supportsOptional = false; 78 for (BluetoothCodecConfig config : mStateMachine.getCodecStatus() 79 .getCodecsSelectableCapabilities()) { 80 if (!config.isMandatoryCodec()) { 81 supportsOptional = true; 82 break; 83 } 84 } 85 if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN 86 || supportsOptional != (previousSupport 87 == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { 88 setSupportsOptionalCodecs(device, supportsOptional); 89 } 90 if (supportsOptional) { 91 int enabled = getOptionalCodecsEnabled(device); 92 if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 93 enableOptionalCodecs(); 94 } else if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED) { 95 disableOptionalCodecs(); 96 } 97 } 98 } 99 } 100 101 ; 102 103 private static A2dpService sA2dpService; 104 static final ParcelUuid[] A2DP_SOURCE_UUID = { 105 BluetoothUuid.AudioSource 106 }; 107 static final ParcelUuid[] A2DP_SOURCE_SINK_UUIDS = { 108 BluetoothUuid.AudioSource, BluetoothUuid.AudioSink 109 }; 110 111 @Override 112 protected String getName() { 113 return TAG; 114 } 115 116 @Override 117 protected IProfileServiceBinder initBinder() { 118 return new BluetoothA2dpBinder(this); 119 } 120 121 @Override 122 protected boolean start() { 123 if (DBG) { 124 Log.d(TAG, "start()"); 125 } 126 127 mStateMachinesThread = new HandlerThread("A2dpService.StateMachines"); 128 mStateMachinesThread.start(); 129 130 mAvrcp = Avrcp.make(this); 131 mStateMachine = A2dpStateMachine.make(this, this, mA2dpNativeInterface, 132 mStateMachinesThread.getLooper()); 133 setA2dpService(this); 134 if (mConnectionStateChangedReceiver == null) { 135 IntentFilter filter = new IntentFilter(); 136 filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 137 mConnectionStateChangedReceiver = new CodecSupportReceiver(); 138 registerReceiver(mConnectionStateChangedReceiver, filter); 139 } 140 return true; 141 } 142 143 @Override 144 protected boolean stop() { 145 if (DBG) { 146 Log.d(TAG, "stop()"); 147 } 148 149 if (mStateMachine != null) { 150 mStateMachine.doQuit(); 151 } 152 if (mAvrcp != null) { 153 mAvrcp.doQuit(); 154 } 155 if (mStateMachinesThread != null) { 156 mStateMachinesThread.quit(); 157 mStateMachinesThread = null; 158 } 159 return true; 160 } 161 162 @Override 163 protected boolean cleanup() { 164 if (DBG) { 165 Log.d(TAG, "cleanup()"); 166 } 167 168 if (mConnectionStateChangedReceiver != null) { 169 unregisterReceiver(mConnectionStateChangedReceiver); 170 mConnectionStateChangedReceiver = null; 171 } 172 if (mStateMachine != null) { 173 mStateMachine.cleanup(); 174 mStateMachine = null; 175 } 176 if (mAvrcp != null) { 177 mAvrcp.cleanup(); 178 mAvrcp = null; 179 } 180 clearA2dpService(); 181 return true; 182 } 183 184 //API Methods 185 186 public static synchronized A2dpService getA2dpService() { 187 if (sA2dpService != null && sA2dpService.isAvailable()) { 188 if (DBG) { 189 Log.d(TAG, "getA2DPService(): returning " + sA2dpService); 190 } 191 return sA2dpService; 192 } 193 if (DBG) { 194 if (sA2dpService == null) { 195 Log.d(TAG, "getA2dpService(): service is NULL"); 196 } else if (!(sA2dpService.isAvailable())) { 197 Log.d(TAG, "getA2dpService(): service is not available"); 198 } 199 } 200 return null; 201 } 202 203 private static synchronized void setA2dpService(A2dpService instance) { 204 if (instance != null && instance.isAvailable()) { 205 if (DBG) { 206 Log.d(TAG, "setA2dpService(): set to: " + instance); 207 } 208 sA2dpService = instance; 209 } else { 210 if (DBG) { 211 if (sA2dpService == null) { 212 Log.d(TAG, "setA2dpService(): service not available"); 213 } else if (!sA2dpService.isAvailable()) { 214 Log.d(TAG, "setA2dpService(): service is cleaning up"); 215 } 216 } 217 } 218 } 219 220 private static synchronized void clearA2dpService() { 221 sA2dpService = null; 222 } 223 224 public boolean connect(BluetoothDevice device) { 225 if (DBG) { 226 Log.d(TAG, "connect(): " + device); 227 } 228 229 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 230 231 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 232 return false; 233 } 234 ParcelUuid[] featureUuids = device.getUuids(); 235 if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) 236 && !(BluetoothUuid.containsAllUuids(featureUuids, A2DP_SOURCE_SINK_UUIDS))) { 237 Log.e(TAG, "Remote does not have A2dp Sink UUID"); 238 return false; 239 } 240 241 int connectionState = mStateMachine.getConnectionState(device); 242 if (connectionState == BluetoothProfile.STATE_CONNECTED 243 || connectionState == BluetoothProfile.STATE_CONNECTING) { 244 return false; 245 } 246 247 mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device); 248 return true; 249 } 250 251 boolean disconnect(BluetoothDevice device) { 252 if (DBG) { 253 Log.d(TAG, "disconnect(): " + device); 254 } 255 256 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 257 int connectionState = mStateMachine.getConnectionState(device); 258 if (connectionState != BluetoothProfile.STATE_CONNECTED 259 && connectionState != BluetoothProfile.STATE_CONNECTING) { 260 return false; 261 } 262 263 mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device); 264 return true; 265 } 266 267 public List<BluetoothDevice> getConnectedDevices() { 268 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 269 return mStateMachine.getConnectedDevices(); 270 } 271 272 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 273 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 274 return mStateMachine.getDevicesMatchingConnectionStates(states); 275 } 276 277 public int getConnectionState(BluetoothDevice device) { 278 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 279 return mStateMachine.getConnectionState(device); 280 } 281 282 public boolean setPriority(BluetoothDevice device, int priority) { 283 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 284 Settings.Global.putInt(getContentResolver(), 285 Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority); 286 if (DBG) { 287 Log.d(TAG, "Saved priority " + device + " = " + priority); 288 } 289 return true; 290 } 291 292 public int getPriority(BluetoothDevice device) { 293 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 294 int priority = Settings.Global.getInt(getContentResolver(), 295 Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()), 296 BluetoothProfile.PRIORITY_UNDEFINED); 297 return priority; 298 } 299 300 /* Absolute volume implementation */ 301 public boolean isAvrcpAbsoluteVolumeSupported() { 302 return mAvrcp.isAbsoluteVolumeSupported(); 303 } 304 305 public void adjustAvrcpAbsoluteVolume(int direction) { 306 mAvrcp.adjustVolume(direction); 307 } 308 309 public void setAvrcpAbsoluteVolume(int volume) { 310 mAvrcp.setAbsoluteVolume(volume); 311 } 312 313 public void setAvrcpAudioState(int state) { 314 mAvrcp.setA2dpAudioState(state); 315 } 316 317 public void resetAvrcpBlacklist(BluetoothDevice device) { 318 if (mAvrcp != null) { 319 mAvrcp.resetBlackList(device.getAddress()); 320 } 321 } 322 323 synchronized boolean isA2dpPlaying(BluetoothDevice device) { 324 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 325 if (DBG) { 326 Log.d(TAG, "isA2dpPlaying(" + device + ")"); 327 } 328 return mStateMachine.isPlaying(device); 329 } 330 331 public BluetoothCodecStatus getCodecStatus() { 332 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 333 if (DBG) { 334 Log.d(TAG, "getCodecStatus()"); 335 } 336 return mStateMachine.getCodecStatus(); 337 } 338 339 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { 340 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 341 if (DBG) { 342 Log.d(TAG, "setCodecConfigPreference(): " + Objects.toString(codecConfig)); 343 } 344 mStateMachine.setCodecConfigPreference(codecConfig); 345 } 346 347 public void enableOptionalCodecs() { 348 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 349 if (DBG) { 350 Log.d(TAG, "enableOptionalCodecs()"); 351 } 352 mStateMachine.enableOptionalCodecs(); 353 } 354 355 public void disableOptionalCodecs() { 356 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 357 if (DBG) { 358 Log.d(TAG, "disableOptionalCodecs()"); 359 } 360 mStateMachine.disableOptionalCodecs(); 361 } 362 363 public int getSupportsOptionalCodecs(BluetoothDevice device) { 364 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 365 int support = Settings.Global.getInt(getContentResolver(), 366 Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()), 367 BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN); 368 return support; 369 } 370 371 public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) { 372 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 373 int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED 374 : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED; 375 Settings.Global.putInt(getContentResolver(), 376 Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()), 377 value); 378 } 379 380 public int getOptionalCodecsEnabled(BluetoothDevice device) { 381 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 382 return Settings.Global.getInt(getContentResolver(), 383 Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()), 384 BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN); 385 } 386 387 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { 388 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 389 if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN 390 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED 391 && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { 392 Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value); 393 return; 394 } 395 Settings.Global.putInt(getContentResolver(), 396 Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()), 397 value); 398 } 399 400 // Handle messages from native (JNI) to Java 401 void messageFromNative(A2dpStackEvent stackEvent) { 402 mStateMachine.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent); 403 } 404 405 // Binder object: Must be static class or memory leak may occur 406 private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 407 implements IProfileServiceBinder { 408 private A2dpService mService; 409 410 private A2dpService getService() { 411 if (!Utils.checkCaller()) { 412 Log.w(TAG, "A2dp call not allowed for non-active user"); 413 return null; 414 } 415 416 if (mService != null && mService.isAvailable()) { 417 return mService; 418 } 419 return null; 420 } 421 422 BluetoothA2dpBinder(A2dpService svc) { 423 mService = svc; 424 } 425 426 @Override 427 public boolean cleanup() { 428 mService = null; 429 return true; 430 } 431 432 @Override 433 public boolean connect(BluetoothDevice device) { 434 A2dpService service = getService(); 435 if (service == null) { 436 return false; 437 } 438 return service.connect(device); 439 } 440 441 @Override 442 public boolean disconnect(BluetoothDevice device) { 443 A2dpService service = getService(); 444 if (service == null) { 445 return false; 446 } 447 return service.disconnect(device); 448 } 449 450 @Override 451 public List<BluetoothDevice> getConnectedDevices() { 452 A2dpService service = getService(); 453 if (service == null) { 454 return new ArrayList<BluetoothDevice>(0); 455 } 456 return service.getConnectedDevices(); 457 } 458 459 @Override 460 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 461 A2dpService service = getService(); 462 if (service == null) { 463 return new ArrayList<BluetoothDevice>(0); 464 } 465 return service.getDevicesMatchingConnectionStates(states); 466 } 467 468 @Override 469 public int getConnectionState(BluetoothDevice device) { 470 A2dpService service = getService(); 471 if (service == null) { 472 return BluetoothProfile.STATE_DISCONNECTED; 473 } 474 return service.getConnectionState(device); 475 } 476 477 @Override 478 public boolean setPriority(BluetoothDevice device, int priority) { 479 A2dpService service = getService(); 480 if (service == null) { 481 return false; 482 } 483 return service.setPriority(device, priority); 484 } 485 486 @Override 487 public int getPriority(BluetoothDevice device) { 488 A2dpService service = getService(); 489 if (service == null) { 490 return BluetoothProfile.PRIORITY_UNDEFINED; 491 } 492 return service.getPriority(device); 493 } 494 495 @Override 496 public boolean isAvrcpAbsoluteVolumeSupported() { 497 A2dpService service = getService(); 498 if (service == null) { 499 return false; 500 } 501 return service.isAvrcpAbsoluteVolumeSupported(); 502 } 503 504 @Override 505 public void adjustAvrcpAbsoluteVolume(int direction) { 506 A2dpService service = getService(); 507 if (service == null) { 508 return; 509 } 510 service.adjustAvrcpAbsoluteVolume(direction); 511 } 512 513 @Override 514 public void setAvrcpAbsoluteVolume(int volume) { 515 A2dpService service = getService(); 516 if (service == null) { 517 return; 518 } 519 service.setAvrcpAbsoluteVolume(volume); 520 } 521 522 @Override 523 public boolean isA2dpPlaying(BluetoothDevice device) { 524 A2dpService service = getService(); 525 if (service == null) { 526 return false; 527 } 528 return service.isA2dpPlaying(device); 529 } 530 531 @Override 532 public BluetoothCodecStatus getCodecStatus() { 533 A2dpService service = getService(); 534 if (service == null) { 535 return null; 536 } 537 return service.getCodecStatus(); 538 } 539 540 @Override 541 public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { 542 A2dpService service = getService(); 543 if (service == null) { 544 return; 545 } 546 service.setCodecConfigPreference(codecConfig); 547 } 548 549 @Override 550 public void enableOptionalCodecs() { 551 A2dpService service = getService(); 552 if (service == null) { 553 return; 554 } 555 service.enableOptionalCodecs(); 556 } 557 558 @Override 559 public void disableOptionalCodecs() { 560 A2dpService service = getService(); 561 if (service == null) { 562 return; 563 } 564 service.disableOptionalCodecs(); 565 } 566 567 public int supportsOptionalCodecs(BluetoothDevice device) { 568 A2dpService service = getService(); 569 if (service == null) { 570 return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; 571 } 572 return service.getSupportsOptionalCodecs(device); 573 } 574 575 public int getOptionalCodecsEnabled(BluetoothDevice device) { 576 A2dpService service = getService(); 577 if (service == null) { 578 return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; 579 } 580 return service.getOptionalCodecsEnabled(device); 581 } 582 583 public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { 584 A2dpService service = getService(); 585 if (service == null) { 586 return; 587 } 588 service.setOptionalCodecsEnabled(device, value); 589 } 590 } 591 592 ; 593 594 @Override 595 public void dump(StringBuilder sb) { 596 super.dump(sb); 597 if (mStateMachine != null) { 598 mStateMachine.dump(sb); 599 } 600 if (mAvrcp != null) { 601 mAvrcp.dump(sb); 602 } 603 } 604} 605