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