1 /* 2 * Copyright (C) 2014 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.avrcp; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothAvrcpController; 21 import android.bluetooth.BluetoothAvrcpPlayerSettings; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothProfile; 24 import android.bluetooth.IBluetoothAvrcpController; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.media.MediaMetadata; 30 import android.media.session.PlaybackState; 31 import android.os.Handler; 32 import android.os.HandlerThread; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.util.Log; 36 import android.media.AudioManager; 37 import com.android.bluetooth.a2dpsink.A2dpSinkService; 38 import com.android.bluetooth.btservice.ProfileService; 39 import com.android.bluetooth.Utils; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.List; 43 import java.util.HashMap; 44 import java.nio.charset.Charset; 45 import java.nio.ByteBuffer; 46 /** 47 * Provides Bluetooth AVRCP Controller profile, as a service in the Bluetooth application. 48 * @hide 49 */ 50 public class AvrcpControllerService extends ProfileService { 51 private static final boolean DBG = AvrcpControllerConstants.DBG; 52 private static final boolean VDBG = AvrcpControllerConstants.VDBG; 53 private static final String TAG = "AvrcpControllerService"; 54 55 /* 56 * Messages handled by mHandler 57 */ 58 59 RemoteDevice mAvrcpRemoteDevice; 60 RemoteMediaPlayers mRemoteMediaPlayers; 61 NowPlaying mRemoteNowPlayingList; 62 63 private AvrcpMessageHandler mHandler; 64 private static AvrcpControllerService sAvrcpControllerService; 65 private static AudioManager mAudioManager; 66 67 private final ArrayList<BluetoothDevice> mConnectedDevices 68 = new ArrayList<BluetoothDevice>(); 69 70 static { 71 classInitNative(); 72 } 73 74 public AvrcpControllerService() { 75 initNative(); 76 } 77 78 protected String getName() { 79 return TAG; 80 } 81 82 protected IProfileServiceBinder initBinder() { 83 return new BluetoothAvrcpControllerBinder(this); 84 } 85 86 protected boolean start() { 87 HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler"); 88 thread.start(); 89 Looper looper = thread.getLooper(); 90 mHandler = new AvrcpMessageHandler(looper); 91 92 setAvrcpControllerService(this); 93 mAudioManager = (AudioManager)sAvrcpControllerService. 94 getSystemService(Context.AUDIO_SERVICE); 95 return true; 96 } 97 98 protected void resetRemoteData() { 99 try { 100 unregisterReceiver(mBroadcastReceiver); 101 } 102 catch (IllegalArgumentException e) { 103 Log.e(TAG,"Receiver not registered"); 104 } 105 if(mAvrcpRemoteDevice != null) { 106 mAvrcpRemoteDevice.cleanup(); 107 mAvrcpRemoteDevice = null; 108 } 109 if(mRemoteMediaPlayers != null) { 110 mRemoteMediaPlayers.cleanup(); 111 mRemoteMediaPlayers = null; 112 } 113 if(mRemoteNowPlayingList != null) { 114 mRemoteNowPlayingList.cleanup(); 115 mRemoteNowPlayingList = null; 116 } 117 } 118 protected boolean stop() { 119 if (mHandler != null) { 120 mHandler.removeCallbacksAndMessages(null); 121 Looper looper = mHandler.getLooper(); 122 if (looper != null) { 123 looper.quit(); 124 } 125 } 126 resetRemoteData(); 127 return true; 128 } 129 130 protected boolean cleanup() { 131 if (mHandler != null) { 132 mHandler.removeCallbacksAndMessages(null); 133 Looper looper = mHandler.getLooper(); 134 if (looper != null) looper.quit(); 135 } 136 resetRemoteData(); 137 clearAvrcpControllerService(); 138 cleanupNative(); 139 140 return true; 141 } 142 143 //API Methods 144 145 public static synchronized AvrcpControllerService getAvrcpControllerService(){ 146 if (sAvrcpControllerService != null && sAvrcpControllerService.isAvailable()) { 147 if (DBG) Log.d(TAG, "getAvrcpControllerService(): returning " 148 + sAvrcpControllerService); 149 return sAvrcpControllerService; 150 } 151 if (DBG) { 152 if (sAvrcpControllerService == null) { 153 Log.d(TAG, "getAvrcpControllerService(): service is NULL"); 154 } else if (!(sAvrcpControllerService.isAvailable())) { 155 Log.d(TAG,"getAvrcpControllerService(): service is not available"); 156 } 157 } 158 return null; 159 } 160 161 private static synchronized void setAvrcpControllerService(AvrcpControllerService instance) { 162 if (instance != null && instance.isAvailable()) { 163 if (DBG) Log.d(TAG, "setAvrcpControllerService(): set to: " + sAvrcpControllerService); 164 sAvrcpControllerService = instance; 165 } else { 166 if (DBG) { 167 if (sAvrcpControllerService == null) { 168 Log.d(TAG, "setAvrcpControllerService(): service not available"); 169 } else if (!sAvrcpControllerService.isAvailable()) { 170 Log.d(TAG,"setAvrcpControllerService(): service is cleaning up"); 171 } 172 } 173 } 174 } 175 176 private static synchronized void clearAvrcpControllerService() { 177 sAvrcpControllerService = null; 178 } 179 180 public List<BluetoothDevice> getConnectedDevices() { 181 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 182 return mConnectedDevices; 183 } 184 185 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 186 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 187 for (int i = 0; i < states.length; i++) { 188 if (states[i] == BluetoothProfile.STATE_CONNECTED) { 189 return mConnectedDevices; 190 } 191 } 192 return new ArrayList<BluetoothDevice>(); 193 } 194 195 int getConnectionState(BluetoothDevice device) { 196 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 197 return (mConnectedDevices.contains(device) ? BluetoothProfile.STATE_CONNECTED 198 : BluetoothProfile.STATE_DISCONNECTED); 199 } 200 201 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { 202 Log.v(TAG, "sendGroupNavigationCmd keyCode: " + keyCode + " keyState: " + keyState); 203 if (device == null) { 204 throw new NullPointerException("device == null"); 205 } 206 if (!(mConnectedDevices.contains(device))) { 207 for (BluetoothDevice cdevice : mConnectedDevices) { 208 Log.e(TAG, "Device: " + cdevice); 209 } 210 Log.e(TAG," Device does not match " + device); 211 return; 212 } 213 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 214 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 215 MESSAGE_SEND_GROUP_NAVIGATION_CMD,keyCode, keyState, device); 216 mHandler.sendMessage(msg); 217 } 218 219 public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { 220 Log.v(TAG, "sendPassThroughCmd keyCode: " + keyCode + " keyState: " + keyState); 221 if (device == null) { 222 throw new NullPointerException("device == null"); 223 } 224 if (!(mConnectedDevices.contains(device))) { 225 Log.d(TAG," Device does not match"); 226 return; 227 } 228 if ((mAvrcpRemoteDevice == null)|| 229 (mAvrcpRemoteDevice.mRemoteFeatures == AvrcpControllerConstants.BTRC_FEAT_NONE)|| 230 (mRemoteMediaPlayers == null) || 231 (mRemoteMediaPlayers.getAddressedPlayer() == null)){ 232 Log.d(TAG," Device connected but PlayState not present "); 233 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 234 Message msg = mHandler.obtainMessage(AvrcpControllerConstants.MESSAGE_SEND_PASS_THROUGH_CMD, 235 keyCode, keyState, device); 236 mHandler.sendMessage(msg); 237 return; 238 } 239 boolean sendCommand = false; 240 switch(keyCode) { 241 case BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY: 242 sendCommand = (mRemoteMediaPlayers.getPlayStatus() == 243 AvrcpControllerConstants.PLAY_STATUS_STOPPED)|| 244 (mRemoteMediaPlayers.getPlayStatus() == 245 AvrcpControllerConstants.PLAY_STATUS_PAUSED) || 246 (mRemoteMediaPlayers.getPlayStatus() == 247 AvrcpControllerConstants.PLAY_STATUS_PLAYING); 248 break; 249 case BluetoothAvrcpController.PASS_THRU_CMD_ID_PAUSE: 250 /* 251 * allowing pause command in pause state to handle A2DP Sink Concurrency 252 * If call is ongoing and Start is initiated from remote, we will send pause again 253 * If acquireFocus fails, we will send Pause again 254 * To Stop sending multiple Pause, check in application. 255 */ 256 sendCommand = (mRemoteMediaPlayers.getPlayStatus() == 257 AvrcpControllerConstants.PLAY_STATUS_PLAYING)|| 258 (mRemoteMediaPlayers.getPlayStatus() == 259 AvrcpControllerConstants.PLAY_STATUS_FWD_SEEK)|| 260 (mRemoteMediaPlayers.getPlayStatus() == 261 AvrcpControllerConstants.PLAY_STATUS_STOPPED)|| 262 (mRemoteMediaPlayers.getPlayStatus() == 263 AvrcpControllerConstants.PLAY_STATUS_PAUSED)|| 264 (mRemoteMediaPlayers.getPlayStatus() == 265 AvrcpControllerConstants.PLAY_STATUS_REV_SEEK); 266 break; 267 case BluetoothAvrcpController.PASS_THRU_CMD_ID_STOP: 268 sendCommand = (mRemoteMediaPlayers.getPlayStatus() == 269 AvrcpControllerConstants.PLAY_STATUS_PLAYING)|| 270 (mRemoteMediaPlayers.getPlayStatus() == 271 AvrcpControllerConstants.PLAY_STATUS_FWD_SEEK)|| 272 (mRemoteMediaPlayers.getPlayStatus() == 273 AvrcpControllerConstants.PLAY_STATUS_REV_SEEK)|| 274 (mRemoteMediaPlayers.getPlayStatus() == 275 AvrcpControllerConstants.PLAY_STATUS_STOPPED)|| 276 (mRemoteMediaPlayers.getPlayStatus() == 277 AvrcpControllerConstants.PLAY_STATUS_PAUSED); 278 break; 279 case BluetoothAvrcpController.PASS_THRU_CMD_ID_BACKWARD: 280 case BluetoothAvrcpController.PASS_THRU_CMD_ID_FORWARD: 281 case BluetoothAvrcpController.PASS_THRU_CMD_ID_FF: 282 case BluetoothAvrcpController.PASS_THRU_CMD_ID_REWIND: 283 sendCommand = true; // we can send this command in all states 284 break; 285 } 286 if (sendCommand) { 287 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 288 Message msg = mHandler.obtainMessage(AvrcpControllerConstants.MESSAGE_SEND_PASS_THROUGH_CMD, 289 keyCode, keyState, device); 290 mHandler.sendMessage(msg); 291 } 292 else { 293 Log.e(TAG," Not in right state, don't send Pass Thru cmd "); 294 } 295 } 296 297 public void startAvrcpUpdates() { 298 mHandler.obtainMessage( 299 AvrcpControllerConstants.MESSAGE_START_METADATA_BROADCASTS).sendToTarget(); 300 } 301 302 public void stopAvrcpUpdates() { 303 mHandler.obtainMessage( 304 AvrcpControllerConstants.MESSAGE_STOP_METADATA_BROADCASTS).sendToTarget(); 305 } 306 307 public MediaMetadata getMetaData(BluetoothDevice device) { 308 Log.d(TAG, "getMetaData = "); 309 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 310 if((mRemoteNowPlayingList != null) && (mRemoteNowPlayingList.getCurrentTrack() != null)) { 311 return getCurrentMetaData(AvrcpControllerConstants.AVRCP_SCOPE_NOW_PLAYING, 0); 312 } 313 else 314 return null; 315 } 316 public PlaybackState getPlaybackState(BluetoothDevice device) { 317 if (DBG) Log.d(TAG, "getPlayBackState device = "+ device); 318 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 319 return getCurrentPlayBackState(); 320 } 321 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { 322 if (DBG) Log.d(TAG, "getPlayerApplicationSetting "); 323 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 324 return getCurrentPlayerAppSetting(); 325 } 326 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { 327 if ((mAvrcpRemoteDevice == null)||(mRemoteMediaPlayers == null)) { 328 return false; 329 } 330 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 331 /* 332 * We have to extract values from BluetoothAvrcpPlayerSettings 333 */ 334 int mSettings = plAppSetting.getSettings(); 335 int numAttributes = 0; 336 /* calculate number of attributes in request */ 337 while(mSettings > 0) { 338 numAttributes += ((mSettings & 0x01)!= 0)?1: 0; 339 mSettings = mSettings >> 1; 340 } 341 byte[] attribArray = new byte [2*numAttributes]; 342 mSettings = plAppSetting.getSettings(); 343 /* 344 * Now we will flatten it <id, val> 345 */ 346 int i = 0; 347 if((mSettings & BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) != 0) { 348 attribArray[i++] = AvrcpControllerConstants.ATTRIB_EQUALIZER_STATUS; 349 attribArray[i++] = (byte)AvrcpUtils.mapAvrcpPlayerSettingstoBTAttribVal( 350 BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER, plAppSetting. 351 getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER)); 352 } 353 if((mSettings & BluetoothAvrcpPlayerSettings.SETTING_REPEAT) != 0) { 354 attribArray[i++] = AvrcpControllerConstants.ATTRIB_REPEAT_STATUS; 355 attribArray[i++] = (byte)AvrcpUtils.mapAvrcpPlayerSettingstoBTAttribVal( 356 BluetoothAvrcpPlayerSettings.SETTING_REPEAT, plAppSetting. 357 getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_REPEAT)); 358 } 359 if((mSettings & BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) != 0) { 360 attribArray[i++] = AvrcpControllerConstants.ATTRIB_SHUFFLE_STATUS; 361 attribArray[i++] = (byte)AvrcpUtils.mapAvrcpPlayerSettingstoBTAttribVal( 362 BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE, plAppSetting. 363 getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE)); 364 } 365 if((mSettings & BluetoothAvrcpPlayerSettings.SETTING_SCAN) != 0) { 366 attribArray[i++] = AvrcpControllerConstants.ATTRIB_SCAN_STATUS; 367 attribArray[i++] = (byte)AvrcpUtils.mapAvrcpPlayerSettingstoBTAttribVal( 368 BluetoothAvrcpPlayerSettings.SETTING_SCAN, plAppSetting. 369 getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SCAN)); 370 } 371 boolean isSettingSupported = mRemoteMediaPlayers.getAddressedPlayer(). 372 isPlayerAppSettingSupported((byte)numAttributes, attribArray); 373 if(isSettingSupported) { 374 ByteBuffer bb = ByteBuffer.wrap(attribArray, 0, (2*numAttributes)); 375 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 376 MESSAGE_SEND_SET_CURRENT_PLAYER_APPLICATION_SETTINGS, numAttributes, 0, bb); 377 mHandler.sendMessage(msg); 378 } 379 return isSettingSupported; 380 } 381 382 //Binder object: Must be static class or memory leak may occur 383 private static class BluetoothAvrcpControllerBinder extends IBluetoothAvrcpController.Stub 384 implements IProfileServiceBinder { 385 private AvrcpControllerService mService; 386 387 private AvrcpControllerService getService() { 388 if (!Utils.checkCaller()) { 389 Log.w(TAG,"AVRCP call not allowed for non-active user"); 390 return null; 391 } 392 393 if (mService != null && mService.isAvailable()) { 394 return mService; 395 } 396 return null; 397 } 398 399 BluetoothAvrcpControllerBinder(AvrcpControllerService svc) { 400 mService = svc; 401 } 402 403 public boolean cleanup() { 404 mService = null; 405 return true; 406 } 407 408 public List<BluetoothDevice> getConnectedDevices() { 409 AvrcpControllerService service = getService(); 410 if (service == null) return new ArrayList<BluetoothDevice>(0); 411 return service.getConnectedDevices(); 412 } 413 414 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 415 AvrcpControllerService service = getService(); 416 if (service == null) return new ArrayList<BluetoothDevice>(0); 417 return service.getDevicesMatchingConnectionStates(states); 418 } 419 420 public int getConnectionState(BluetoothDevice device) { 421 AvrcpControllerService service = getService(); 422 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 423 return service.getConnectionState(device); 424 } 425 426 public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { 427 Log.v(TAG,"Binder Call: sendPassThroughCmd"); 428 AvrcpControllerService service = getService(); 429 if (service == null) return; 430 service.sendPassThroughCmd(device, keyCode, keyState); 431 } 432 433 @Override 434 public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { 435 Log.v(TAG,"Binder Call: sendGroupNavigationCmd"); 436 AvrcpControllerService service = getService(); 437 if (service == null) return; 438 service.sendGroupNavigationCmd(device, keyCode, keyState); 439 } 440 441 @Override 442 public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { 443 Log.v(TAG,"Binder Call: getPlayerApplicationSetting "); 444 AvrcpControllerService service = getService(); 445 if (service == null) return null; 446 return service.getPlayerSettings(device); 447 } 448 449 @Override 450 public MediaMetadata getMetadata(BluetoothDevice device) { 451 Log.v(TAG,"Binder Call: getMetaData "); 452 AvrcpControllerService service = getService(); 453 if (service == null) return null; 454 return service.getMetaData(device); 455 } 456 457 @Override 458 public PlaybackState getPlaybackState(BluetoothDevice device) { 459 Log.v(TAG,"Binder Call: getPlaybackState"); 460 AvrcpControllerService service = getService(); 461 if (service == null) return null; 462 return service.getPlaybackState(device); 463 } 464 465 @Override 466 public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { 467 Log.v(TAG,"Binder Call: setPlayerApplicationSetting " ); 468 AvrcpControllerService service = getService(); 469 if (service == null) return false; 470 return service.setPlayerApplicationSetting(plAppSetting); 471 } 472 }; 473 474 private String utf8ToString(byte[] input) 475 { 476 Charset UTF8_CHARSET = Charset.forName("UTF-8"); 477 return new String(input,UTF8_CHARSET); 478 } 479 private int asciiToInt(int len, byte[] array) 480 { 481 return Integer.parseInt(utf8ToString(array)); 482 } 483 private BluetoothAvrcpPlayerSettings getCurrentPlayerAppSetting() { 484 if((mRemoteMediaPlayers == null) || (mRemoteMediaPlayers.getAddressedPlayer() == null)) 485 return null; 486 return mRemoteMediaPlayers.getAddressedPlayer().getSupportedPlayerAppSetting(); 487 } 488 private PlaybackState getCurrentPlayBackState() { 489 if ((mRemoteMediaPlayers == null) || (mRemoteMediaPlayers.getAddressedPlayer() == null)) { 490 return new PlaybackState.Builder().setState(PlaybackState.STATE_ERROR, 491 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 492 0).build(); 493 } 494 return AvrcpUtils.mapBtPlayStatustoPlayBackState( 495 mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus, 496 mRemoteMediaPlayers.getAddressedPlayer().mPlayTime); 497 } 498 private MediaMetadata getCurrentMetaData(int scope, int trackId) { 499 /* if scope is now playing */ 500 if(scope == AvrcpControllerConstants.AVRCP_SCOPE_NOW_PLAYING) { 501 if((mRemoteNowPlayingList == null) || (mRemoteNowPlayingList. 502 getTrackFromId(trackId) == null)) 503 return null; 504 TrackInfo mNowPlayingTrack = mRemoteNowPlayingList.getTrackFromId(trackId); 505 return AvrcpUtils.getMediaMetaData(mNowPlayingTrack); 506 } 507 /* if scope is now playing */ 508 else if(scope == AvrcpControllerConstants.AVRCP_SCOPE_VFS) { 509 /* TODO for browsing */ 510 } 511 return null; 512 } 513 private void broadcastMetaDataChanged(MediaMetadata mMetaData) { 514 Intent intent = new Intent(BluetoothAvrcpController.ACTION_TRACK_EVENT); 515 intent.putExtra(BluetoothAvrcpController.EXTRA_METADATA, mMetaData); 516 if(DBG) Log.d(TAG," broadcastMetaDataChanged = " + 517 AvrcpUtils.displayMetaData(mMetaData)); 518 sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 519 } 520 private void broadcastPlayBackStateChanged(PlaybackState state) { 521 Intent intent = new Intent(BluetoothAvrcpController.ACTION_TRACK_EVENT); 522 intent.putExtra(BluetoothAvrcpController.EXTRA_PLAYBACK, state); 523 if(DBG) Log.d(TAG," broadcastPlayBackStateChanged = " + state.toString()); 524 sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 525 } 526 private void broadcastPlayerAppSettingChanged(BluetoothAvrcpPlayerSettings mPlAppSetting) { 527 Intent intent = new Intent(BluetoothAvrcpController.ACTION_PLAYER_SETTING); 528 intent.putExtra(BluetoothAvrcpController.EXTRA_PLAYER_SETTING, mPlAppSetting); 529 if(DBG) Log.d(TAG," broadcastPlayerAppSettingChanged = " + 530 AvrcpUtils.displayBluetoothAvrcpSettings(mPlAppSetting)); 531 sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 532 } 533 /** Handles Avrcp messages. */ 534 private final class AvrcpMessageHandler extends Handler { 535 private boolean mBroadcastMetadata = false; 536 537 private AvrcpMessageHandler(Looper looper) { 538 super(looper); 539 } 540 541 @Override 542 public void handleMessage(Message msg) { 543 Log.d(TAG," HandleMessage: "+ AvrcpControllerConstants.dumpMessageString(msg.what) + 544 " Remote Connected " + !mConnectedDevices.isEmpty()); 545 A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); 546 switch (msg.what) { 547 case AvrcpControllerConstants.MESSAGE_STOP_METADATA_BROADCASTS: 548 // Any messages hence forth about play pos/play status/song info will be ignored. 549 if(mRemoteMediaPlayers != null) { 550 // Mock the current state to *look like* it is paused. The remote play state is 551 // still cached in mRemoteMediaPlayers and will be restored when the 552 // startAvrcpUpdates is called again. 553 broadcastPlayBackStateChanged(AvrcpUtils.mapBtPlayStatustoPlayBackState 554 ((byte) AvrcpControllerConstants.PLAY_STATUS_PAUSED, 555 mRemoteMediaPlayers.getAddressedPlayer().mPlayTime)); 556 } 557 mBroadcastMetadata = false; 558 break; 559 case AvrcpControllerConstants.MESSAGE_START_METADATA_BROADCASTS: 560 // Any messages hence forth about play pos/play status/song info will be sent. 561 if(mRemoteMediaPlayers != null) { 562 broadcastPlayBackStateChanged(getCurrentPlayBackState()); 563 broadcastMetaDataChanged( 564 getCurrentMetaData(AvrcpControllerConstants.AVRCP_SCOPE_NOW_PLAYING, 0)); 565 } 566 mBroadcastMetadata = true; 567 break; 568 case AvrcpControllerConstants.MESSAGE_SEND_PASS_THROUGH_CMD: 569 BluetoothDevice device = (BluetoothDevice)msg.obj; 570 sendPassThroughCommandNative(getByteAddress(device), msg.arg1, msg.arg2); 571 if((a2dpSinkService != null)&&(!mConnectedDevices.isEmpty())) { 572 Log.d(TAG," inform AVRCP Commands to A2DP Sink "); 573 a2dpSinkService.informAvrcpPassThroughCmd(device, msg.arg1, msg.arg2); 574 } 575 break; 576 case AvrcpControllerConstants.MESSAGE_SEND_GROUP_NAVIGATION_CMD: 577 BluetoothDevice peerDevice = (BluetoothDevice)msg.obj; 578 sendGroupNavigationCommandNative(getByteAddress(peerDevice), msg.arg1, msg.arg2); 579 break; 580 case AvrcpControllerConstants.MESSAGE_SEND_SET_CURRENT_PLAYER_APPLICATION_SETTINGS: 581 byte numAttributes = (byte)msg.arg1; 582 ByteBuffer bbRsp = (ByteBuffer)msg.obj; 583 byte[] attributeIds = new byte [numAttributes]; 584 byte[] attributeVals = new byte [numAttributes]; 585 for(int i = 0; (bbRsp.hasRemaining())&&(i < numAttributes); i++) { 586 attributeIds[i] = bbRsp.get(); 587 attributeVals[i] = bbRsp.get(); 588 } 589 setPlayerApplicationSettingValuesNative(getByteAddress(mAvrcpRemoteDevice.mBTDevice), 590 numAttributes, attributeIds, attributeVals); 591 break; 592 593 case AvrcpControllerConstants.MESSAGE_PROCESS_CONNECTION_CHANGE: 594 int newState = msg.arg1; 595 int oldState = msg.arg2; 596 BluetoothDevice rtDevice = (BluetoothDevice)msg.obj; 597 if ((newState == BluetoothProfile.STATE_CONNECTED) && 598 (oldState == BluetoothProfile.STATE_DISCONNECTED)) { 599 /* We create RemoteDevice and MediaPlayerList here 600 * Now playing list after RC features 601 */ 602 if(mAvrcpRemoteDevice == null){ 603 mAvrcpRemoteDevice = new RemoteDevice(rtDevice); 604 /* Remote will have a player irrespective of AVRCP Version 605 * Create a Default player, we will add entries in case Browsing 606 * is supported by remote 607 */ 608 if(mRemoteMediaPlayers == null) { 609 mRemoteMediaPlayers = new RemoteMediaPlayers(mAvrcpRemoteDevice); 610 PlayerInfo mPlayer = new PlayerInfo(); 611 mPlayer.mPlayerId = 0; 612 mRemoteMediaPlayers.addPlayer(mPlayer); 613 mRemoteMediaPlayers.setAddressedPlayer(mPlayer); 614 } 615 } 616 } 617 else if ((newState == BluetoothProfile.STATE_DISCONNECTED) && 618 (oldState == BluetoothProfile.STATE_CONNECTED)) /* connection down */ 619 { 620 resetRemoteData(); 621 mHandler.removeCallbacksAndMessages(null); 622 } 623 /* 624 * Send intent now 625 */ 626 Intent intent = new Intent(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED); 627 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, oldState); 628 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 629 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, rtDevice); 630 // intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 631 sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 632 break; 633 case AvrcpControllerConstants.MESSAGE_PROCESS_RC_FEATURES: 634 if(mAvrcpRemoteDevice == null) 635 break; 636 mAvrcpRemoteDevice.mRemoteFeatures = msg.arg1; 637 /* in case of AVRCP version < 1.3, no need to add track info */ 638 if(mAvrcpRemoteDevice.isMetaDataSupported()) { 639 if(mRemoteNowPlayingList == null) 640 mRemoteNowPlayingList = new NowPlaying(mAvrcpRemoteDevice); 641 TrackInfo mTrack = new TrackInfo(); 642 /* First element of NowPlayingList will be current Track 643 * for 1.3 this will be the only song 644 * for >= 1.4, others songs will have non-zero UID 645 */ 646 mTrack.mItemUid = 0; 647 mRemoteNowPlayingList.addTrack(mTrack); 648 mRemoteNowPlayingList.setCurrTrack(mTrack); 649 } 650 break; 651 case AvrcpControllerConstants.MESSAGE_PROCESS_SET_ABS_VOL_CMD: 652 mAvrcpRemoteDevice.mAbsVolNotificationState = 653 AvrcpControllerConstants.DEFER_VOLUME_CHANGE_RSP; 654 setAbsVolume(msg.arg1, msg.arg2); 655 break; 656 case AvrcpControllerConstants.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: 657 /* start BroadcastReceiver now */ 658 IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION); 659 mAvrcpRemoteDevice.mNotificationLabel = msg.arg1; 660 mAvrcpRemoteDevice.mAbsVolNotificationState = 661 AvrcpControllerConstants.SEND_VOLUME_CHANGE_RSP; 662 registerReceiver(mBroadcastReceiver, filter); 663 int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 664 int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 665 int percentageVol = ((currIndex* AvrcpControllerConstants.ABS_VOL_BASE)/maxVolume); 666 Log.d(TAG," Sending Interim Response = "+ percentageVol + " label " + msg.arg1); 667 sendRegisterAbsVolRspNative(getByteAddress(mAvrcpRemoteDevice.mBTDevice), 668 (byte)AvrcpControllerConstants.NOTIFICATION_RSP_TYPE_INTERIM, percentageVol, 669 mAvrcpRemoteDevice.mNotificationLabel); 670 break; 671 case AvrcpControllerConstants.MESSAGE_PROCESS_TRACK_CHANGED: 672 if(mRemoteNowPlayingList != null) { 673 mRemoteNowPlayingList.updateCurrentTrack((TrackInfo)msg.obj); 674 675 if (!mBroadcastMetadata) { 676 Log.d(TAG, "Metadata is not broadcasted, ignoring."); 677 return; 678 } 679 680 broadcastMetaDataChanged(AvrcpUtils.getMediaMetaData 681 (mRemoteNowPlayingList.getCurrentTrack())); 682 } 683 break; 684 case AvrcpControllerConstants.MESSAGE_PROCESS_PLAY_POS_CHANGED: 685 if(mRemoteMediaPlayers != null) { 686 mRemoteMediaPlayers.getAddressedPlayer().mPlayTime = msg.arg2; 687 688 if (!mBroadcastMetadata) { 689 Log.d(TAG, "Metadata is not broadcasted, ignoring."); 690 return; 691 } 692 693 broadcastPlayBackStateChanged(AvrcpUtils.mapBtPlayStatustoPlayBackState 694 (mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus, 695 mRemoteMediaPlayers.getAddressedPlayer().mPlayTime)); 696 } 697 if(mRemoteNowPlayingList != null) { 698 mRemoteNowPlayingList.getCurrentTrack().mTrackLen = msg.arg1; 699 } 700 break; 701 case AvrcpControllerConstants.MESSAGE_PROCESS_PLAY_STATUS_CHANGED: 702 if(mRemoteMediaPlayers != null) { 703 int status = msg.arg1; 704 mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus = (byte) status; 705 if (status == AvrcpControllerConstants.PLAY_STATUS_PLAYING) { 706 a2dpSinkService.informTGStatePlaying(mConnectedDevices.get(0), true); 707 } else if (status == AvrcpControllerConstants.PLAY_STATUS_PAUSED || 708 status == AvrcpControllerConstants.PLAY_STATUS_STOPPED) { 709 a2dpSinkService.informTGStatePlaying(mConnectedDevices.get(0), false); 710 } 711 712 if (mBroadcastMetadata) { 713 broadcastPlayBackStateChanged(AvrcpUtils.mapBtPlayStatustoPlayBackState 714 (mRemoteMediaPlayers.getAddressedPlayer().mPlayStatus, 715 mRemoteMediaPlayers.getAddressedPlayer().mPlayTime)); 716 } else { 717 Log.d(TAG, "Metadata is not broadcasted, ignoring."); 718 return; 719 } 720 } 721 break; 722 case AvrcpControllerConstants.MESSAGE_PROCESS_SUPPORTED_PLAYER_APP_SETTING: 723 if(mRemoteMediaPlayers != null) 724 mRemoteMediaPlayers.getAddressedPlayer(). 725 setSupportedPlayerAppSetting((ByteBuffer)msg.obj); 726 break; 727 case AvrcpControllerConstants.MESSAGE_PROCESS_PLAYER_APP_SETTING_CHANGED: 728 if(mRemoteMediaPlayers != null) { 729 mRemoteMediaPlayers.getAddressedPlayer(). 730 updatePlayerAppSetting((ByteBuffer)msg.obj); 731 broadcastPlayerAppSettingChanged(getCurrentPlayerAppSetting()); 732 } 733 break; 734 } 735 } 736 } 737 738 private void setAbsVolume(int absVol, int label) 739 { 740 int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 741 int currIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 742 if (mAvrcpRemoteDevice.mFirstAbsVolCmdRecvd) { 743 int newIndex = (maxVolume*absVol)/AvrcpControllerConstants.ABS_VOL_BASE; 744 Log.d(TAG," setAbsVolume ="+absVol + " maxVol = " + maxVolume + " cur = " + currIndex + 745 " new = "+newIndex); 746 /* 747 * In some cases change in percentage is not sufficient enough to warrant 748 * change in index values which are in range of 0-15. For such cases 749 * no action is required 750 */ 751 if (newIndex != currIndex) { 752 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newIndex, 753 AudioManager.FLAG_SHOW_UI); 754 } 755 } 756 else { 757 mAvrcpRemoteDevice.mFirstAbsVolCmdRecvd = true; 758 absVol = (currIndex*AvrcpControllerConstants.ABS_VOL_BASE)/maxVolume; 759 Log.d(TAG," SetAbsVol recvd for first time, respond with " + absVol); 760 } 761 sendAbsVolRspNative(getByteAddress(mAvrcpRemoteDevice.mBTDevice), absVol, label); 762 } 763 764 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 765 @Override 766 public void onReceive(Context context, Intent intent) { 767 String action = intent.getAction(); 768 if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { 769 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 770 if (streamType == AudioManager.STREAM_MUSIC) { 771 int streamValue = intent 772 .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); 773 int streamPrevValue = intent.getIntExtra( 774 AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1); 775 if (streamValue != -1 && streamValue != streamPrevValue) { 776 if ((mAvrcpRemoteDevice == null) 777 ||((mAvrcpRemoteDevice.mRemoteFeatures & 778 AvrcpControllerConstants.BTRC_FEAT_ABSOLUTE_VOLUME) == 0) 779 ||(mConnectedDevices.isEmpty())) 780 return; 781 if(mAvrcpRemoteDevice.mAbsVolNotificationState == 782 AvrcpControllerConstants.SEND_VOLUME_CHANGE_RSP) { 783 int maxVol = mAudioManager. 784 getStreamMaxVolume(AudioManager.STREAM_MUSIC); 785 int currIndex = mAudioManager. 786 getStreamVolume(AudioManager.STREAM_MUSIC); 787 int percentageVol = ((currIndex* 788 AvrcpControllerConstants.ABS_VOL_BASE)/maxVol); 789 Log.d(TAG," Abs Vol Notify Rsp Changed vol = "+ percentageVol); 790 sendRegisterAbsVolRspNative(getByteAddress(mAvrcpRemoteDevice.mBTDevice), 791 (byte)AvrcpControllerConstants.NOTIFICATION_RSP_TYPE_CHANGED, 792 percentageVol, mAvrcpRemoteDevice.mNotificationLabel); 793 } 794 else if (mAvrcpRemoteDevice.mAbsVolNotificationState == 795 AvrcpControllerConstants.DEFER_VOLUME_CHANGE_RSP) { 796 Log.d(TAG," Don't Complete Notification Rsp. "); 797 mAvrcpRemoteDevice.mAbsVolNotificationState = 798 AvrcpControllerConstants.SEND_VOLUME_CHANGE_RSP; 799 } 800 } 801 } 802 } 803 } 804 }; 805 806 private void handlePassthroughRsp(int id, int keyState) { 807 Log.d(TAG, "passthrough response received as: key: " + id + " state: " + keyState); 808 } 809 810 private void onConnectionStateChanged(boolean connected, byte[] address) { 811 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 812 (Utils.getAddressStringFromByte(address)); 813 Log.d(TAG, "onConnectionStateChanged " + connected + " " + device+ " size "+ 814 mConnectedDevices.size()); 815 if (device == null) 816 return; 817 int oldState = (mConnectedDevices.contains(device) ? BluetoothProfile.STATE_CONNECTED 818 : BluetoothProfile.STATE_DISCONNECTED); 819 int newState = (connected ? BluetoothProfile.STATE_CONNECTED 820 : BluetoothProfile.STATE_DISCONNECTED); 821 822 if (connected && oldState == BluetoothProfile.STATE_DISCONNECTED) { 823 /* AVRCPControllerService supports single connection */ 824 if(mConnectedDevices.size() > 0) { 825 Log.d(TAG,"A Connection already exists, returning"); 826 return; 827 } 828 mConnectedDevices.add(device); 829 Message msg = mHandler.obtainMessage( 830 AvrcpControllerConstants.MESSAGE_PROCESS_CONNECTION_CHANGE, newState, 831 oldState, device); 832 mHandler.sendMessage(msg); 833 } else if (!connected && oldState == BluetoothProfile.STATE_CONNECTED) { 834 mConnectedDevices.remove(device); 835 Message msg = mHandler.obtainMessage( 836 AvrcpControllerConstants.MESSAGE_PROCESS_CONNECTION_CHANGE, newState, 837 oldState, device); 838 mHandler.sendMessage(msg); 839 } 840 } 841 842 private void getRcFeatures(byte[] address, int features) { 843 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 844 (Utils.getAddressStringFromByte(address)); 845 Message msg = mHandler.obtainMessage( 846 AvrcpControllerConstants.MESSAGE_PROCESS_RC_FEATURES, features, 0, device); 847 mHandler.sendMessage(msg); 848 } 849 private void setPlayerAppSettingRsp(byte[] address, byte accepted) { 850 /* TODO do we need to do anything here */ 851 } 852 private void handleRegisterNotificationAbsVol(byte[] address, byte label) 853 { 854 Log.d(TAG,"handleRegisterNotificationAbsVol "); 855 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 856 (Utils.getAddressStringFromByte(address)); 857 if (!mConnectedDevices.contains(device)) 858 return; 859 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 860 MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION, label, 0); 861 mHandler.sendMessage(msg); 862 } 863 864 private void handleSetAbsVolume(byte[] address, byte absVol, byte label) 865 { 866 Log.d(TAG,"handleSetAbsVolume "); 867 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 868 (Utils.getAddressStringFromByte(address)); 869 if (!mConnectedDevices.contains(device)) 870 return; 871 Message msg = mHandler.obtainMessage( 872 AvrcpControllerConstants.MESSAGE_PROCESS_SET_ABS_VOL_CMD, absVol, label); 873 mHandler.sendMessage(msg); 874 } 875 876 private void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, 877 String[] attribVals) 878 { 879 Log.d(TAG,"onTrackChanged "); 880 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 881 (Utils.getAddressStringFromByte(address)); 882 if (!mConnectedDevices.contains(device)) 883 return; 884 TrackInfo mTrack = new TrackInfo(0, numAttributes, attributes, attribVals); 885 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 886 MESSAGE_PROCESS_TRACK_CHANGED, numAttributes, 0, mTrack); 887 mHandler.sendMessage(msg); 888 } 889 890 private void onPlayPositionChanged(byte[] address, int songLen, int currSongPosition) { 891 Log.d(TAG,"onPlayPositionChanged "); 892 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 893 (Utils.getAddressStringFromByte(address)); 894 if (!mConnectedDevices.contains(device)) 895 return; 896 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 897 MESSAGE_PROCESS_PLAY_POS_CHANGED, songLen, currSongPosition); 898 mHandler.sendMessage(msg); 899 } 900 901 private void onPlayStatusChanged(byte[] address, byte playStatus) { 902 if(DBG) Log.d(TAG,"onPlayStatusChanged " + playStatus); 903 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 904 (Utils.getAddressStringFromByte(address)); 905 if (!mConnectedDevices.contains(device)) 906 return; 907 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 908 MESSAGE_PROCESS_PLAY_STATUS_CHANGED, playStatus, 0); 909 mHandler.sendMessage(msg); 910 } 911 912 private void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen) { 913 Log.d(TAG,"handlePlayerAppSetting rspLen = " + rspLen); 914 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 915 (Utils.getAddressStringFromByte(address)); 916 if (!mConnectedDevices.contains(device)) 917 return; 918 ByteBuffer bb = ByteBuffer.wrap(playerAttribRsp, 0, rspLen); 919 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 920 MESSAGE_PROCESS_SUPPORTED_PLAYER_APP_SETTING, 0, 0, bb); 921 mHandler.sendMessage(msg); 922 } 923 924 private void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen) { 925 Log.d(TAG,"onPlayerAppSettingChanged "); 926 BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice 927 (Utils.getAddressStringFromByte(address)); 928 if (!mConnectedDevices.contains(device)) 929 return; 930 ByteBuffer bb = ByteBuffer.wrap(playerAttribRsp, 0, rspLen); 931 Message msg = mHandler.obtainMessage(AvrcpControllerConstants. 932 MESSAGE_PROCESS_PLAYER_APP_SETTING_CHANGED, 0, 0, bb); 933 mHandler.sendMessage(msg); 934 } 935 936 private void handleGroupNavigationRsp(int id, int keyState) { 937 Log.d(TAG, "group navigation response received as: key: " 938 + id + " state: " + keyState); 939 } 940 941 private byte[] getByteAddress(BluetoothDevice device) { 942 return Utils.getBytesFromAddress(device.getAddress()); 943 } 944 945 @Override 946 public void dump(StringBuilder sb) { 947 super.dump(sb); 948 } 949 950 private native static void classInitNative(); 951 private native void initNative(); 952 private native void cleanupNative(); 953 private native boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState); 954 private native boolean sendGroupNavigationCommandNative(byte[] address, int keyCode, 955 int keyState); 956 private native void setPlayerApplicationSettingValuesNative(byte[] address, byte numAttrib, 957 byte[] atttibIds, byte[]attribVal); 958 /* This api is used to send response to SET_ABS_VOL_CMD */ 959 private native void sendAbsVolRspNative(byte[] address, int absVol, int label); 960 /* This api is used to inform remote for any volume level changes */ 961 private native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol, 962 int label); 963 } 964