1 /* 2 * Copyright (C) 2016 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.bluetooth.avrcpcontroller; 18 19 import android.annotation.RequiresPermission; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothAvrcpPlayerSettings; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothProfile; 24 import android.bluetooth.IBluetoothAvrcpController; 25 import android.content.AttributionSource; 26 import android.content.Intent; 27 import android.support.v4.media.MediaBrowserCompat.MediaItem; 28 import android.support.v4.media.session.PlaybackStateCompat; 29 import android.sysprop.BluetoothProperties; 30 import android.util.Log; 31 32 import com.android.bluetooth.BluetoothPrefs; 33 import com.android.bluetooth.R; 34 import com.android.bluetooth.Utils; 35 import com.android.bluetooth.a2dpsink.A2dpSinkService; 36 import com.android.bluetooth.btservice.AdapterService; 37 import com.android.bluetooth.btservice.ProfileService; 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.modules.utils.SynchronousResultReceiver; 40 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.UUID; 46 import java.util.concurrent.ConcurrentHashMap; 47 48 /** 49 * Provides Bluetooth AVRCP Controller profile, as a service in the Bluetooth application. 50 */ 51 public class AvrcpControllerService extends ProfileService { 52 static final String TAG = "AvrcpControllerService"; 53 static final int MAXIMUM_CONNECTED_DEVICES = 5; 54 static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 55 static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); 56 57 /** 58 * Owned Components 59 */ 60 private static final String ON_ERROR_SETTINGS_ACTIVITY = 61 BluetoothPrefs.class.getCanonicalName(); 62 private static final String COVER_ART_PROVIDER = AvrcpCoverArtProvider.class.getCanonicalName(); 63 64 /* 65 * Play State Values from JNI 66 */ 67 private static final byte JNI_PLAY_STATUS_STOPPED = 0x00; 68 private static final byte JNI_PLAY_STATUS_PLAYING = 0x01; 69 private static final byte JNI_PLAY_STATUS_PAUSED = 0x02; 70 private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03; 71 @VisibleForTesting 72 static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04; 73 private static final byte JNI_PLAY_STATUS_ERROR = -1; 74 75 /* Folder/Media Item scopes. 76 * Keep in sync with AVRCP 1.6 sec. 6.10.1 77 */ 78 public static final byte BROWSE_SCOPE_PLAYER_LIST = 0x00; 79 public static final byte BROWSE_SCOPE_VFS = 0x01; 80 public static final byte BROWSE_SCOPE_SEARCH = 0x02; 81 public static final byte BROWSE_SCOPE_NOW_PLAYING = 0x03; 82 83 /* Folder navigation directions 84 * This is borrowed from AVRCP 1.6 spec and must be kept with same values 85 */ 86 public static final byte FOLDER_NAVIGATION_DIRECTION_UP = 0x00; 87 public static final byte FOLDER_NAVIGATION_DIRECTION_DOWN = 0x01; 88 89 /* 90 * KeyCoded for Pass Through Commands 91 */ 92 public static final int PASS_THRU_CMD_ID_PLAY = 0x44; 93 public static final int PASS_THRU_CMD_ID_PAUSE = 0x46; 94 public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41; 95 public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42; 96 public static final int PASS_THRU_CMD_ID_STOP = 0x45; 97 public static final int PASS_THRU_CMD_ID_FF = 0x49; 98 public static final int PASS_THRU_CMD_ID_REWIND = 0x48; 99 public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B; 100 public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C; 101 102 /* Key State Variables */ 103 public static final int KEY_STATE_PRESSED = 0; 104 public static final int KEY_STATE_RELEASED = 1; 105 106 /* Active Device State Variables */ 107 public static final int DEVICE_STATE_INACTIVE = 0; 108 public static final int DEVICE_STATE_ACTIVE = 1; 109 110 static BrowseTree sBrowseTree; 111 private static AvrcpControllerService sService; 112 113 private AdapterService mAdapterService; 114 115 protected Map<BluetoothDevice, AvrcpControllerStateMachine> mDeviceStateMap = 116 new ConcurrentHashMap<>(1); 117 private BluetoothDevice mActiveDevice = null; 118 private final Object mActiveDeviceLock = new Object(); 119 120 private boolean mCoverArtEnabled = false; 121 protected AvrcpCoverArtManager mCoverArtManager; 122 123 private class ImageDownloadCallback implements AvrcpCoverArtManager.Callback { 124 @Override onImageDownloadComplete(BluetoothDevice device, AvrcpCoverArtManager.DownloadEvent event)125 public void onImageDownloadComplete(BluetoothDevice device, 126 AvrcpCoverArtManager.DownloadEvent event) { 127 if (DBG) { 128 Log.d(TAG, "Image downloaded [device: " + device + ", uuid: " + event.getUuid() 129 + ", uri: " + event.getUri()); 130 } 131 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 132 if (stateMachine == null) { 133 Log.e(TAG, "No state machine found for device " + device); 134 mCoverArtManager.removeImage(device, event.getUuid()); 135 return; 136 } 137 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_IMAGE_DOWNLOADED, 138 event); 139 } 140 } 141 142 static { classInitNative()143 classInitNative(); 144 } 145 isEnabled()146 public static boolean isEnabled() { 147 return BluetoothProperties.isProfileAvrcpControllerEnabled().orElse(false); 148 } 149 150 @Override start()151 protected synchronized boolean start() { 152 initNative(); 153 setComponentAvailable(ON_ERROR_SETTINGS_ACTIVITY, true); 154 mAdapterService = AdapterService.getAdapterService(); 155 mCoverArtEnabled = getResources().getBoolean(R.bool.avrcp_controller_enable_cover_art); 156 if (mCoverArtEnabled) { 157 setComponentAvailable(COVER_ART_PROVIDER, true); 158 mCoverArtManager = new AvrcpCoverArtManager(this, new ImageDownloadCallback()); 159 } 160 sBrowseTree = new BrowseTree(null); 161 sService = this; 162 163 // Start the media browser service. 164 Intent startIntent = new Intent(this, BluetoothMediaBrowserService.class); 165 startService(startIntent); 166 setActiveDevice(null); 167 return true; 168 } 169 170 @Override stop()171 protected synchronized boolean stop() { 172 setActiveDevice(null); 173 Intent stopIntent = new Intent(this, BluetoothMediaBrowserService.class); 174 stopService(stopIntent); 175 for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { 176 stateMachine.quitNow(); 177 } 178 mDeviceStateMap.clear(); 179 180 sService = null; 181 sBrowseTree = null; 182 if (mCoverArtManager != null) { 183 mCoverArtManager.cleanup(); 184 mCoverArtManager = null; 185 setComponentAvailable(COVER_ART_PROVIDER, false); 186 } 187 setComponentAvailable(ON_ERROR_SETTINGS_ACTIVITY, false); 188 return true; 189 } 190 getAvrcpControllerService()191 public static AvrcpControllerService getAvrcpControllerService() { 192 return sService; 193 } 194 195 /** 196 * Get the current active device 197 */ getActiveDevice()198 public BluetoothDevice getActiveDevice() { 199 synchronized (mActiveDeviceLock) { 200 return mActiveDevice; 201 } 202 } 203 204 /** 205 * Set the current active device, notify devices of activity status 206 */ 207 @VisibleForTesting setActiveDevice(BluetoothDevice device)208 boolean setActiveDevice(BluetoothDevice device) { 209 A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); 210 if (a2dpSinkService == null) { 211 return false; 212 } 213 214 BluetoothDevice currentActiveDevice = getActiveDevice(); 215 if ((device == null && currentActiveDevice == null) 216 || (device != null && device.equals(currentActiveDevice))) { 217 return true; 218 } 219 220 // Try and update the active device 221 synchronized (mActiveDeviceLock) { 222 if (a2dpSinkService.setActiveDevice(device)) { 223 mActiveDevice = device; 224 225 // Pause the old active device 226 if (currentActiveDevice != null) { 227 AvrcpControllerStateMachine oldStateMachine = 228 getStateMachine(currentActiveDevice); 229 if (oldStateMachine != null) { 230 oldStateMachine.setDeviceState(DEVICE_STATE_INACTIVE); 231 } 232 } 233 234 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 235 if (stateMachine != null) { 236 stateMachine.setDeviceState(DEVICE_STATE_ACTIVE); 237 } else { 238 BluetoothMediaBrowserService.reset(); 239 } 240 return true; 241 } 242 } 243 return false; 244 } 245 toPlaybackStateFromJni(int fromJni)246 private int toPlaybackStateFromJni(int fromJni) { 247 int playbackState = PlaybackStateCompat.STATE_NONE; 248 switch (fromJni) { 249 case JNI_PLAY_STATUS_STOPPED: 250 playbackState = PlaybackStateCompat.STATE_STOPPED; 251 break; 252 case JNI_PLAY_STATUS_PLAYING: 253 playbackState = PlaybackStateCompat.STATE_PLAYING; 254 break; 255 case JNI_PLAY_STATUS_PAUSED: 256 playbackState = PlaybackStateCompat.STATE_PAUSED; 257 break; 258 case JNI_PLAY_STATUS_FWD_SEEK: 259 playbackState = PlaybackStateCompat.STATE_FAST_FORWARDING; 260 break; 261 case JNI_PLAY_STATUS_REV_SEEK: 262 playbackState = PlaybackStateCompat.STATE_REWINDING; 263 break; 264 default: 265 playbackState = PlaybackStateCompat.STATE_NONE; 266 } 267 return playbackState; 268 } 269 newStateMachine(BluetoothDevice device)270 protected AvrcpControllerStateMachine newStateMachine(BluetoothDevice device) { 271 return new AvrcpControllerStateMachine(device, this); 272 } 273 getCurrentMetadataIfNoCoverArt(BluetoothDevice device)274 protected void getCurrentMetadataIfNoCoverArt(BluetoothDevice device) { 275 if (device == null) return; 276 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 277 if (stateMachine == null) return; 278 AvrcpItem track = stateMachine.getCurrentTrack(); 279 if (track != null && track.getCoverArtLocation() == null) { 280 getCurrentMetadataNative(Utils.getByteAddress(device)); 281 } 282 } 283 284 @VisibleForTesting refreshContents(BrowseTree.BrowseNode node)285 void refreshContents(BrowseTree.BrowseNode node) { 286 BluetoothDevice device = node.getDevice(); 287 if (device == null) { 288 return; 289 } 290 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 291 if (stateMachine != null) { 292 stateMachine.requestContents(node); 293 } 294 } 295 playItem(String parentMediaId)296 void playItem(String parentMediaId) { 297 if (DBG) Log.d(TAG, "playItem(" + parentMediaId + ")"); 298 // Check if the requestedNode is a player rather than a song 299 BrowseTree.BrowseNode requestedNode = sBrowseTree.findBrowseNodeByID(parentMediaId); 300 if (requestedNode == null) { 301 for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { 302 // Check each state machine for the song and then play it 303 requestedNode = stateMachine.findNode(parentMediaId); 304 if (requestedNode != null) { 305 if (DBG) Log.d(TAG, "Found a node"); 306 BluetoothDevice device = stateMachine.getDevice(); 307 if (device != null) { 308 setActiveDevice(device); 309 } 310 stateMachine.playItem(requestedNode); 311 break; 312 } 313 } 314 } 315 } 316 317 /*Java API*/ 318 319 /** 320 * Get a List of MediaItems that are children of the specified media Id 321 * 322 * @param parentMediaId The player or folder to get the contents of 323 * @return List of Children if available, an empty list if there are none, 324 * or null if a search must be performed. 325 */ getContents(String parentMediaId)326 public synchronized List<MediaItem> getContents(String parentMediaId) { 327 if (DBG) Log.d(TAG, "getContents(" + parentMediaId + ")"); 328 329 BrowseTree.BrowseNode requestedNode = sBrowseTree.findBrowseNodeByID(parentMediaId); 330 if (requestedNode == null) { 331 for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { 332 requestedNode = stateMachine.findNode(parentMediaId); 333 if (requestedNode != null) { 334 Log.d(TAG, "Found a node"); 335 break; 336 } 337 } 338 } 339 340 // If we don't find a node in the tree then do not have any way to browse for the contents. 341 // Return an empty list instead. 342 if (requestedNode == null) { 343 if (DBG) Log.d(TAG, "Didn't find a node"); 344 return new ArrayList(0); 345 } else { 346 // If we found a node and it belongs to a device then go ahead and make it active 347 BluetoothDevice device = requestedNode.getDevice(); 348 if (device != null) { 349 setActiveDevice(device); 350 } 351 352 if (!requestedNode.isCached()) { 353 if (DBG) Log.d(TAG, "node is not cached"); 354 refreshContents(requestedNode); 355 } 356 if (DBG) Log.d(TAG, "Returning contents"); 357 return requestedNode.getContents(); 358 } 359 } 360 361 @Override initBinder()362 protected IProfileServiceBinder initBinder() { 363 return new AvrcpControllerServiceBinder(this); 364 } 365 366 //Binder object: Must be static class or memory leak may occur 367 @VisibleForTesting 368 static class AvrcpControllerServiceBinder extends IBluetoothAvrcpController.Stub 369 implements IProfileServiceBinder { 370 private AvrcpControllerService mService; 371 372 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)373 private AvrcpControllerService getService(AttributionSource source) { 374 if (Utils.isInstrumentationTestMode()) { 375 return mService; 376 } 377 if (!Utils.checkServiceAvailable(mService, TAG) 378 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 379 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 380 return null; 381 } 382 return mService; 383 } 384 AvrcpControllerServiceBinder(AvrcpControllerService service)385 AvrcpControllerServiceBinder(AvrcpControllerService service) { 386 mService = service; 387 } 388 389 @Override cleanup()390 public void cleanup() { 391 mService = null; 392 } 393 394 @Override getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)395 public void getConnectedDevices(AttributionSource source, 396 SynchronousResultReceiver receiver) { 397 try { 398 AvrcpControllerService service = getService(source); 399 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0); 400 if (service != null) { 401 defaultValue = service.getConnectedDevices(); 402 } 403 receiver.send(defaultValue); 404 } catch (RuntimeException e) { 405 receiver.propagateException(e); 406 } 407 } 408 409 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)410 public void getDevicesMatchingConnectionStates(int[] states, 411 AttributionSource source, SynchronousResultReceiver receiver) { 412 try { 413 AvrcpControllerService service = getService(source); 414 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0); 415 if (service != null) { 416 defaultValue = service.getDevicesMatchingConnectionStates(states); 417 } 418 receiver.send(defaultValue); 419 } catch (RuntimeException e) { 420 receiver.propagateException(e); 421 } 422 } 423 424 @Override getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)425 public void getConnectionState(BluetoothDevice device, AttributionSource source, 426 SynchronousResultReceiver receiver) { 427 try { 428 AvrcpControllerService service = getService(source); 429 int defaultValue = BluetoothProfile.STATE_DISCONNECTED; 430 if (service != null) { 431 defaultValue = service.getConnectionState(device); 432 } 433 receiver.send(defaultValue); 434 } catch (RuntimeException e) { 435 receiver.propagateException(e); 436 } 437 } 438 439 @Override sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState, AttributionSource source, SynchronousResultReceiver receiver)440 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState, 441 AttributionSource source, SynchronousResultReceiver receiver) { 442 try { 443 AvrcpControllerService service = getService(source); 444 Log.w(TAG, "sendGroupNavigationCmd not implemented"); 445 receiver.send(null); 446 } catch (RuntimeException e) { 447 receiver.propagateException(e); 448 } 449 } 450 451 @Override setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings settings, AttributionSource source, SynchronousResultReceiver receiver)452 public void setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings settings, 453 AttributionSource source, SynchronousResultReceiver receiver) { 454 try { 455 AvrcpControllerService service = getService(source); 456 Log.w(TAG, "setPlayerApplicationSetting not implemented"); 457 receiver.send(null); 458 } catch (RuntimeException e) { 459 receiver.propagateException(e); 460 } 461 } 462 463 @Override getPlayerSettings(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)464 public void getPlayerSettings(BluetoothDevice device, 465 AttributionSource source, SynchronousResultReceiver receiver) { 466 try { 467 AvrcpControllerService service = getService(source); 468 Log.w(TAG, "getPlayerSettings not implemented"); 469 receiver.send(null); 470 } catch (RuntimeException e) { 471 receiver.propagateException(e); 472 } 473 } 474 } 475 476 477 /* JNI API*/ 478 // Called by JNI when a passthrough key was received. 479 @VisibleForTesting handlePassthroughRsp(int id, int keyState, byte[] address)480 void handlePassthroughRsp(int id, int keyState, byte[] address) { 481 if (DBG) { 482 Log.d(TAG, "passthrough response received as: key: " + id 483 + " state: " + keyState + "address:" + Arrays.toString(address)); 484 } 485 } 486 487 @VisibleForTesting handleGroupNavigationRsp(int id, int keyState)488 void handleGroupNavigationRsp(int id, int keyState) { 489 if (DBG) { 490 Log.d(TAG, "group navigation response received as: key: " + id + " state: " 491 + keyState); 492 } 493 } 494 495 // Called by JNI when a device has connected or disconnected. 496 @VisibleForTesting onConnectionStateChanged(boolean remoteControlConnected, boolean browsingConnected, byte[] address)497 synchronized void onConnectionStateChanged(boolean remoteControlConnected, 498 boolean browsingConnected, byte[] address) { 499 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 500 if (DBG) { 501 Log.d(TAG, "onConnectionStateChanged " + remoteControlConnected + " " 502 + browsingConnected + device); 503 } 504 505 StackEvent event = 506 StackEvent.connectionStateChanged(remoteControlConnected, browsingConnected); 507 AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device); 508 if (remoteControlConnected || browsingConnected) { 509 stateMachine.connect(event); 510 // The first device to connect gets to be the active device 511 if (getActiveDevice() == null) { 512 setActiveDevice(device); 513 } 514 } else { 515 stateMachine.disconnect(); 516 if (device.equals(getActiveDevice())) { 517 setActiveDevice(null); 518 } 519 } 520 } 521 522 // Called by JNI to notify Avrcp of features supported by the Remote device. 523 @VisibleForTesting getRcFeatures(byte[] address, int features)524 void getRcFeatures(byte[] address, int features) { 525 /* Do Nothing. */ 526 } 527 528 // Called by JNI to notify Avrcp of a remote device's Cover Art PSM 529 @VisibleForTesting getRcPsm(byte[] address, int psm)530 void getRcPsm(byte[] address, int psm) { 531 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 532 if (DBG) Log.d(TAG, "getRcPsm(device=" + device + ", psm=" + psm + ")"); 533 AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device); 534 if (stateMachine != null) { 535 stateMachine.sendMessage( 536 AvrcpControllerStateMachine.MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM, psm); 537 } 538 } 539 540 // Called by JNI 541 @VisibleForTesting setPlayerAppSettingRsp(byte[] address, byte accepted)542 void setPlayerAppSettingRsp(byte[] address, byte accepted) { 543 /* Do Nothing. */ 544 } 545 546 // Called by JNI when remote wants to receive absolute volume notifications. 547 @VisibleForTesting handleRegisterNotificationAbsVol(byte[] address, byte label)548 synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) { 549 if (DBG) { 550 Log.d(TAG, "handleRegisterNotificationAbsVol"); 551 } 552 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 553 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 554 if (stateMachine != null) { 555 stateMachine.sendMessage( 556 AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION); 557 } 558 } 559 560 // Called by JNI when remote wants to set absolute volume. 561 @VisibleForTesting handleSetAbsVolume(byte[] address, byte absVol, byte label)562 synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) { 563 if (DBG) { 564 Log.d(TAG, "handleSetAbsVolume "); 565 } 566 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 567 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 568 if (stateMachine != null) { 569 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, 570 absVol); 571 } 572 } 573 574 // Called by JNI when a track changes and local AvrcpController is registered for updates. 575 @VisibleForTesting onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals)576 synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, 577 String[] attribVals) { 578 if (DBG) { 579 Log.d(TAG, "onTrackChanged"); 580 } 581 582 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 583 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 584 if (stateMachine != null) { 585 AvrcpItem.Builder aib = new AvrcpItem.Builder(); 586 aib.fromAvrcpAttributeArray(attributes, attribVals); 587 aib.setDevice(device); 588 aib.setItemType(AvrcpItem.TYPE_MEDIA); 589 aib.setUuid(UUID.randomUUID().toString()); 590 AvrcpItem item = aib.build(); 591 if (mCoverArtManager != null) { 592 String handle = item.getCoverArtHandle(); 593 if (handle != null) { 594 item.setCoverArtUuid(mCoverArtManager.getUuidForHandle(device, handle)); 595 } 596 } 597 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED, 598 item); 599 } 600 } 601 602 // Called by JNI periodically based upon timer to update play position 603 @VisibleForTesting onPlayPositionChanged(byte[] address, int songLen, int currSongPosition)604 synchronized void onPlayPositionChanged(byte[] address, int songLen, 605 int currSongPosition) { 606 if (DBG) { 607 Log.d(TAG, "onPlayPositionChanged pos " + currSongPosition); 608 } 609 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 610 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 611 if (stateMachine != null) { 612 stateMachine.sendMessage( 613 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_POS_CHANGED, 614 songLen, currSongPosition); 615 } 616 } 617 618 // Called by JNI on changes of play status 619 @VisibleForTesting onPlayStatusChanged(byte[] address, byte playStatus)620 synchronized void onPlayStatusChanged(byte[] address, byte playStatus) { 621 if (DBG) { 622 Log.d(TAG, "onPlayStatusChanged " + playStatus); 623 } 624 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 625 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 626 if (stateMachine != null) { 627 stateMachine.sendMessage( 628 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, 629 toPlaybackStateFromJni(playStatus)); 630 } 631 } 632 633 // Called by JNI to report remote Player's capabilities 634 @VisibleForTesting handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen)635 synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, 636 int rspLen) { 637 if (DBG) { 638 Log.d(TAG, "handlePlayerAppSetting rspLen = " + rspLen); 639 } 640 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 641 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 642 if (stateMachine != null) { 643 PlayerApplicationSettings supportedSettings = 644 PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp); 645 stateMachine.sendMessage( 646 AvrcpControllerStateMachine.MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS, 647 supportedSettings); 648 } 649 } 650 651 @VisibleForTesting onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen)652 synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, 653 int rspLen) { 654 if (DBG) { 655 Log.d(TAG, "onPlayerAppSettingChanged "); 656 } 657 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 658 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 659 if (stateMachine != null) { 660 661 PlayerApplicationSettings currentSettings = 662 PlayerApplicationSettings.makeSettings(playerAttribRsp); 663 stateMachine.sendMessage( 664 AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS, 665 currentSettings); 666 } 667 } 668 669 @VisibleForTesting onAvailablePlayerChanged(byte[] address)670 void onAvailablePlayerChanged(byte[] address) { 671 if (DBG) { 672 Log.d(TAG," onAvailablePlayerChanged"); 673 } 674 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 675 676 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 677 if (stateMachine != null) { 678 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED); 679 } 680 } 681 682 // Browsing related JNI callbacks. handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items)683 void handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items) { 684 if (DBG) { 685 Log.d(TAG, "handleGetFolderItemsRsp called with status " + status + " items " 686 + items.length + " items."); 687 } 688 689 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 690 List<AvrcpItem> itemsList = new ArrayList<>(); 691 for (AvrcpItem item : items) { 692 if (VDBG) Log.d(TAG, item.toString()); 693 if (mCoverArtManager != null) { 694 String handle = item.getCoverArtHandle(); 695 if (handle != null) { 696 item.setCoverArtUuid(mCoverArtManager.getUuidForHandle(device, handle)); 697 } 698 } 699 itemsList.add(item); 700 } 701 702 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 703 if (stateMachine != null) { 704 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS, 705 itemsList); 706 } 707 } 708 handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items)709 void handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items) { 710 if (DBG) { 711 Log.d(TAG, "handleGetFolderItemsRsp called with " + items.length + " items."); 712 } 713 714 List<AvrcpPlayer> itemsList = new ArrayList<>(); 715 for (AvrcpPlayer item : items) { 716 if (VDBG) Log.d(TAG, "bt player item: " + item); 717 itemsList.add(item); 718 } 719 720 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 721 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 722 if (stateMachine != null) { 723 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, 724 itemsList); 725 } 726 } 727 728 // JNI Helper functions to convert native objects to java. createFromNativeMediaItem(byte[] address, long uid, int type, String name, int[] attrIds, String[] attrVals)729 AvrcpItem createFromNativeMediaItem(byte[] address, long uid, int type, String name, 730 int[] attrIds, String[] attrVals) { 731 if (VDBG) { 732 Log.d(TAG, "createFromNativeMediaItem uid: " + uid + " type: " + type + " name: " + name 733 + " attrids: " + Arrays.toString(attrIds) 734 + " attrVals: " + Arrays.toString(attrVals)); 735 } 736 737 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 738 AvrcpItem.Builder aib = new AvrcpItem.Builder().fromAvrcpAttributeArray(attrIds, attrVals); 739 aib.setDevice(device); 740 aib.setItemType(AvrcpItem.TYPE_MEDIA); 741 aib.setType(type); 742 aib.setUid(uid); 743 aib.setUuid(UUID.randomUUID().toString()); 744 aib.setPlayable(true); 745 AvrcpItem item = aib.build(); 746 return item; 747 } 748 createFromNativeFolderItem(byte[] address, long uid, int type, String name, int playable)749 AvrcpItem createFromNativeFolderItem(byte[] address, long uid, int type, String name, 750 int playable) { 751 if (VDBG) { 752 Log.d(TAG, "createFromNativeFolderItem uid: " + uid + " type " + type + " name " 753 + name + " playable " + playable); 754 } 755 756 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 757 AvrcpItem.Builder aib = new AvrcpItem.Builder(); 758 aib.setDevice(device); 759 aib.setItemType(AvrcpItem.TYPE_FOLDER); 760 aib.setType(type); 761 aib.setUid(uid); 762 aib.setUuid(UUID.randomUUID().toString()); 763 aib.setDisplayableName(name); 764 aib.setPlayable(playable == 0x01); 765 aib.setBrowsable(true); 766 return aib.build(); 767 } 768 createFromNativePlayerItem(byte[] address, int id, String name, byte[] transportFlags, int playStatus, int playerType)769 AvrcpPlayer createFromNativePlayerItem(byte[] address, int id, String name, 770 byte[] transportFlags, int playStatus, int playerType) { 771 if (VDBG) { 772 Log.d(TAG, "createFromNativePlayerItem name: " + name 773 + " transportFlags " + Arrays.toString(transportFlags) 774 + " play status " + playStatus 775 + " player type " + playerType); 776 } 777 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 778 AvrcpPlayer.Builder apb = new AvrcpPlayer.Builder(); 779 apb.setDevice(device); 780 apb.setPlayerId(id); 781 apb.setPlayerType(playerType); 782 apb.setSupportedFeatures(transportFlags); 783 apb.setName(name); 784 apb.setPlayStatus(toPlaybackStateFromJni(playStatus)); 785 return apb.build(); 786 } 787 788 @VisibleForTesting handleChangeFolderRsp(byte[] address, int count)789 void handleChangeFolderRsp(byte[] address, int count) { 790 if (DBG) { 791 Log.d(TAG, "handleChangeFolderRsp count: " + count); 792 } 793 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 794 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 795 if (stateMachine != null) { 796 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, 797 count); 798 } 799 } 800 801 @VisibleForTesting handleSetBrowsedPlayerRsp(byte[] address, int items, int depth)802 void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) { 803 if (DBG) { 804 Log.d(TAG, "handleSetBrowsedPlayerRsp depth: " + depth); 805 } 806 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 807 808 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 809 if (stateMachine != null) { 810 stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER, 811 items, depth); 812 } 813 } 814 815 @VisibleForTesting handleSetAddressedPlayerRsp(byte[] address, int status)816 void handleSetAddressedPlayerRsp(byte[] address, int status) { 817 if (DBG) { 818 Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status); 819 } 820 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 821 822 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 823 if (stateMachine != null) { 824 stateMachine.sendMessage( 825 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER); 826 } 827 } 828 829 @VisibleForTesting handleAddressedPlayerChanged(byte[] address, int id)830 void handleAddressedPlayerChanged(byte[] address, int id) { 831 if (DBG) { 832 Log.d(TAG, "handleAddressedPlayerChanged id: " + id); 833 } 834 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 835 836 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 837 if (stateMachine != null) { 838 stateMachine.sendMessage( 839 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, id); 840 } 841 } 842 843 @VisibleForTesting handleNowPlayingContentChanged(byte[] address)844 void handleNowPlayingContentChanged(byte[] address) { 845 if (DBG) { 846 Log.d(TAG, "handleNowPlayingContentChanged"); 847 } 848 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 849 850 AvrcpControllerStateMachine stateMachine = getStateMachine(device); 851 if (stateMachine != null) { 852 stateMachine.nowPlayingContentChanged(); 853 } 854 } 855 856 /* Generic Profile Code */ 857 858 /** 859 * Disconnect the given Bluetooth device. 860 * 861 * @return true if disconnect is successful, false otherwise. 862 */ disconnect(BluetoothDevice device)863 public synchronized boolean disconnect(BluetoothDevice device) { 864 if (DBG) { 865 StringBuilder sb = new StringBuilder(); 866 dump(sb); 867 Log.d(TAG, "MAP disconnect device: " + device 868 + ", InstanceMap start state: " + sb.toString()); 869 } 870 AvrcpControllerStateMachine stateMachine = mDeviceStateMap.get(device); 871 // a map state machine instance doesn't exist. maybe it is already gone? 872 if (stateMachine == null) { 873 return false; 874 } 875 int connectionState = stateMachine.getState(); 876 if (connectionState != BluetoothProfile.STATE_CONNECTED 877 && connectionState != BluetoothProfile.STATE_CONNECTING) { 878 return false; 879 } 880 stateMachine.disconnect(); 881 if (DBG) { 882 StringBuilder sb = new StringBuilder(); 883 dump(sb); 884 Log.d(TAG, "MAP disconnect device: " + device 885 + ", InstanceMap start state: " + sb.toString()); 886 } 887 return true; 888 } 889 890 /** 891 * Remove state machine from device map once it is no longer needed. 892 */ removeStateMachine(AvrcpControllerStateMachine stateMachine)893 public void removeStateMachine(AvrcpControllerStateMachine stateMachine) { 894 BluetoothDevice device = stateMachine.getDevice(); 895 if (device.equals(getActiveDevice())) { 896 setActiveDevice(null); 897 } 898 mDeviceStateMap.remove(stateMachine.getDevice()); 899 } 900 getConnectedDevices()901 public List<BluetoothDevice> getConnectedDevices() { 902 return getDevicesMatchingConnectionStates(new int[]{BluetoothAdapter.STATE_CONNECTED}); 903 } 904 getStateMachine(BluetoothDevice device)905 protected AvrcpControllerStateMachine getStateMachine(BluetoothDevice device) { 906 if (device == null) { 907 return null; 908 } 909 return mDeviceStateMap.get(device); 910 } 911 getOrCreateStateMachine(BluetoothDevice device)912 protected AvrcpControllerStateMachine getOrCreateStateMachine(BluetoothDevice device) { 913 AvrcpControllerStateMachine stateMachine = mDeviceStateMap.get(device); 914 if (stateMachine == null) { 915 stateMachine = newStateMachine(device); 916 mDeviceStateMap.put(device, stateMachine); 917 stateMachine.start(); 918 } 919 return stateMachine; 920 } 921 getCoverArtManager()922 protected AvrcpCoverArtManager getCoverArtManager() { 923 return mCoverArtManager; 924 } 925 getDevicesMatchingConnectionStates(int[] states)926 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 927 if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states)); 928 List<BluetoothDevice> deviceList = new ArrayList<>(); 929 BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 930 int connectionState; 931 for (BluetoothDevice device : bondedDevices) { 932 connectionState = getConnectionState(device); 933 if (DBG) Log.d(TAG, "Device: " + device + "State: " + connectionState); 934 for (int i = 0; i < states.length; i++) { 935 if (connectionState == states[i]) { 936 deviceList.add(device); 937 } 938 } 939 } 940 if (DBG) Log.d(TAG, deviceList.toString()); 941 Log.d(TAG, "GetDevicesDone"); 942 return deviceList; 943 } 944 getConnectionState(BluetoothDevice device)945 synchronized int getConnectionState(BluetoothDevice device) { 946 AvrcpControllerStateMachine stateMachine = mDeviceStateMap.get(device); 947 return (stateMachine == null) ? BluetoothProfile.STATE_DISCONNECTED 948 : stateMachine.getState(); 949 } 950 951 @Override dump(StringBuilder sb)952 public void dump(StringBuilder sb) { 953 super.dump(sb); 954 ProfileService.println(sb, "Devices Tracked = " + mDeviceStateMap.size()); 955 ProfileService.println(sb, "Active Device = " + mActiveDevice); 956 957 for (AvrcpControllerStateMachine stateMachine : mDeviceStateMap.values()) { 958 ProfileService.println(sb, 959 "==== StateMachine for " + stateMachine.getDevice() + " ===="); 960 stateMachine.dump(sb); 961 } 962 sb.append("\n sBrowseTree: " + sBrowseTree.toString()); 963 964 sb.append("\n Cover Artwork Enabled: " + (mCoverArtEnabled ? "True" : "False")); 965 if (mCoverArtManager != null) { 966 sb.append("\n " + mCoverArtManager.toString()); 967 } 968 969 sb.append("\n " + BluetoothMediaBrowserService.dump() + "\n"); 970 } 971 972 /*JNI*/ classInitNative()973 private static native void classInitNative(); 974 initNative()975 private native void initNative(); 976 cleanupNative()977 private native void cleanupNative(); 978 979 /** 980 * Send button press commands to addressed device 981 * 982 * @param keyCode key code as defined in AVRCP specification 983 * @param keyState 0 = key pressed, 1 = key released 984 * @return command was sent 985 */ sendPassThroughCommandNative(byte[] address, int keyCode, int keyState)986 public native boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState); 987 988 /** 989 * Send group navigation commands 990 * 991 * @param keyCode next/previous 992 * @param keyState state 993 * @return command was sent 994 */ sendGroupNavigationCommandNative(byte[] address, int keyCode, int keyState)995 public native boolean sendGroupNavigationCommandNative(byte[] address, int keyCode, 996 int keyState); 997 998 /** 999 * Change player specific settings such as shuffle 1000 * 1001 * @param numAttrib number of settings being sent 1002 * @param attribIds list of settings to be changed 1003 * @param attribVal list of settings values 1004 */ setPlayerApplicationSettingValuesNative(byte[] address, byte numAttrib, byte[] attribIds, byte[] attribVal)1005 public native void setPlayerApplicationSettingValuesNative(byte[] address, byte numAttrib, 1006 byte[] attribIds, byte[] attribVal); 1007 1008 /** 1009 * Send response to set absolute volume 1010 * 1011 * @param absVol new volume 1012 * @param label label 1013 */ sendAbsVolRspNative(byte[] address, int absVol, int label)1014 public native void sendAbsVolRspNative(byte[] address, int absVol, int label); 1015 1016 /** 1017 * Register for any volume level changes 1018 * 1019 * @param rspType type of response 1020 * @param absVol current volume 1021 * @param label label 1022 */ sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol, int label)1023 public native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol, 1024 int label); 1025 1026 /** 1027 * Fetch the current track's metadata 1028 * 1029 * This method is specifically meant to allow us to fetch image handles that may not have been 1030 * sent to us yet, prior to having a BIP client connection. See the AVRCP 1.6+ specification, 1031 * section 4.1.7, for more details. 1032 */ getCurrentMetadataNative(byte[] address)1033 public native void getCurrentMetadataNative(byte[] address); 1034 1035 /** 1036 * Fetch the playback state 1037 */ getPlaybackStateNative(byte[] address)1038 public native void getPlaybackStateNative(byte[] address); 1039 1040 /** 1041 * Fetch the current now playing list 1042 * 1043 * @param start first index to retrieve 1044 * @param end last index to retrieve 1045 */ getNowPlayingListNative(byte[] address, int start, int end)1046 public native void getNowPlayingListNative(byte[] address, int start, int end); 1047 1048 /** 1049 * Fetch the current folder's listing 1050 * 1051 * @param start first index to retrieve 1052 * @param end last index to retrieve 1053 */ getFolderListNative(byte[] address, int start, int end)1054 public native void getFolderListNative(byte[] address, int start, int end); 1055 1056 /** 1057 * Fetch the listing of players 1058 * 1059 * @param start first index to retrieve 1060 * @param end last index to retrieve 1061 */ getPlayerListNative(byte[] address, int start, int end)1062 public native void getPlayerListNative(byte[] address, int start, int end); 1063 1064 /** 1065 * Change the current browsed folder 1066 * 1067 * @param direction up/down 1068 * @param uid folder unique id 1069 */ changeFolderPathNative(byte[] address, byte direction, long uid)1070 public native void changeFolderPathNative(byte[] address, byte direction, long uid); 1071 1072 /** 1073 * Play item with provided uid 1074 * 1075 * @param scope scope of item to played 1076 * @param uid song unique id 1077 * @param uidCounter counter 1078 */ playItemNative(byte[] address, byte scope, long uid, int uidCounter)1079 public native void playItemNative(byte[] address, byte scope, long uid, int uidCounter); 1080 1081 /** 1082 * Set a specific player for browsing 1083 * 1084 * @param playerId player number 1085 */ setBrowsedPlayerNative(byte[] address, int playerId)1086 public native void setBrowsedPlayerNative(byte[] address, int playerId); 1087 1088 /** 1089 * Set a specific player for handling playback commands 1090 * 1091 * @param playerId player number 1092 */ setAddressedPlayerNative(byte[] address, int playerId)1093 public native void setAddressedPlayerNative(byte[] address, int playerId); 1094 } 1095