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 17package com.android.bluetooth.avrcp; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.bluetooth.BluetoothA2dp; 22import android.bluetooth.BluetoothAvrcp; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.SharedPreferences; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.PackageManager; 31import android.content.pm.PackageManager.NameNotFoundException; 32import android.content.pm.ResolveInfo; 33import android.content.res.Resources; 34import android.media.AudioManager; 35import android.media.AudioPlaybackConfiguration; 36import android.media.MediaDescription; 37import android.media.MediaMetadata; 38import android.media.session.MediaSession; 39import android.media.session.MediaSessionManager; 40import android.media.session.PlaybackState; 41import android.os.Bundle; 42import android.os.Handler; 43import android.os.HandlerThread; 44import android.os.Looper; 45import android.os.Message; 46import android.os.SystemClock; 47import android.os.UserManager; 48import android.util.Log; 49import android.view.KeyEvent; 50 51import com.android.bluetooth.R; 52import com.android.bluetooth.Utils; 53import com.android.bluetooth.btservice.ProfileService; 54 55import java.util.ArrayList; 56import java.util.Collections; 57import java.util.HashMap; 58import java.util.HashSet; 59import java.util.Iterator; 60import java.util.List; 61import java.util.Map; 62import java.util.Set; 63import java.util.SortedMap; 64import java.util.TreeMap; 65 66/****************************************************************************** 67 * support Bluetooth AVRCP profile. support metadata, play status, event 68 * notifications, address player selection and browse feature implementation. 69 ******************************************************************************/ 70 71public final class Avrcp { 72 private static final boolean DEBUG = false; 73 private static final String TAG = "Avrcp"; 74 private static final String ABSOLUTE_VOLUME_BLACKLIST = "absolute_volume_blacklist"; 75 76 private Context mContext; 77 private final AudioManager mAudioManager; 78 private volatile AvrcpMessageHandler mHandler; 79 private Handler mAudioManagerPlaybackHandler; 80 private AudioManagerPlaybackListener mAudioManagerPlaybackCb; 81 private MediaSessionManager mMediaSessionManager; 82 @Nullable private MediaController mMediaController; 83 private MediaControllerListener mMediaControllerCb; 84 private MediaAttributes mMediaAttributes; 85 private long mLastQueueId; 86 private PackageManager mPackageManager; 87 private int mTransportControlFlags; 88 @NonNull private PlaybackState mCurrentPlayState; 89 private int mA2dpState; 90 private boolean mAudioManagerIsPlaying; 91 private int mPlayStatusChangedNT; 92 private byte mReportedPlayStatus; 93 private int mTrackChangedNT; 94 private int mPlayPosChangedNT; 95 private int mAddrPlayerChangedNT; 96 private int mReportedPlayerID; 97 private int mNowPlayingListChangedNT; 98 private long mPlaybackIntervalMs; 99 private long mLastReportedPosition; 100 private long mNextPosMs; 101 private long mPrevPosMs; 102 private int mFeatures; 103 private int mRemoteVolume; 104 private int mLastRemoteVolume; 105 private int mInitialRemoteVolume; 106 107 /* Local volume in audio index 0-15 */ 108 private int mLocalVolume; 109 private int mLastLocalVolume; 110 private int mAbsVolThreshold; 111 112 private String mAddress; 113 private HashMap<Integer, Integer> mVolumeMapping; 114 115 private int mLastDirection; 116 private final int mVolumeStep; 117 private final int mAudioStreamMax; 118 private boolean mVolCmdSetInProgress; 119 private int mAbsVolRetryTimes; 120 121 private static final int NO_PLAYER_ID = 0; 122 123 private int mCurrAddrPlayerID; 124 private int mCurrBrowsePlayerID; 125 private int mLastUsedPlayerID; 126 private AvrcpMediaRsp mAvrcpMediaRsp; 127 128 /* UID counter to be shared across different files. */ 129 static short sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER; 130 131 /* BTRC features */ 132 public static final int BTRC_FEAT_METADATA = 0x01; 133 public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02; 134 public static final int BTRC_FEAT_BROWSE = 0x04; 135 136 /* AVRC response codes, from avrc_defs */ 137 private static final int AVRC_RSP_NOT_IMPL = 8; 138 private static final int AVRC_RSP_ACCEPT = 9; 139 private static final int AVRC_RSP_REJ = 10; 140 private static final int AVRC_RSP_IN_TRANS = 11; 141 private static final int AVRC_RSP_IMPL_STBL = 12; 142 private static final int AVRC_RSP_CHANGED = 13; 143 private static final int AVRC_RSP_INTERIM = 15; 144 145 /* AVRC request commands from Native */ 146 private static final int MSG_NATIVE_REQ_GET_RC_FEATURES = 1; 147 private static final int MSG_NATIVE_REQ_GET_PLAY_STATUS = 2; 148 private static final int MSG_NATIVE_REQ_GET_ELEM_ATTRS = 3; 149 private static final int MSG_NATIVE_REQ_REGISTER_NOTIFICATION = 4; 150 private static final int MSG_NATIVE_REQ_VOLUME_CHANGE = 5; 151 private static final int MSG_NATIVE_REQ_GET_FOLDER_ITEMS = 6; 152 private static final int MSG_NATIVE_REQ_SET_ADDR_PLAYER = 7; 153 private static final int MSG_NATIVE_REQ_SET_BR_PLAYER = 8; 154 private static final int MSG_NATIVE_REQ_CHANGE_PATH = 9; 155 private static final int MSG_NATIVE_REQ_PLAY_ITEM = 10; 156 private static final int MSG_NATIVE_REQ_GET_ITEM_ATTR = 11; 157 private static final int MSG_NATIVE_REQ_GET_TOTAL_NUM_OF_ITEMS = 12; 158 private static final int MSG_NATIVE_REQ_PASS_THROUGH = 13; 159 160 /* other AVRC messages */ 161 private static final int MSG_PLAY_INTERVAL_TIMEOUT = 14; 162 private static final int MSG_SET_ABSOLUTE_VOLUME = 16; 163 private static final int MSG_ABS_VOL_TIMEOUT = 17; 164 private static final int MSG_SET_A2DP_AUDIO_STATE = 18; 165 private static final int MSG_NOW_PLAYING_CHANGED_RSP = 19; 166 167 private static final int CMD_TIMEOUT_DELAY = 2000; 168 private static final int MAX_ERROR_RETRY_TIMES = 6; 169 private static final int AVRCP_MAX_VOL = 127; 170 private static final int AVRCP_BASE_VOLUME_STEP = 1; 171 172 /* Communicates with MediaPlayer to fetch media content */ 173 private BrowsedMediaPlayer mBrowsedMediaPlayer; 174 175 /* Addressed player handling */ 176 private AddressedMediaPlayer mAddressedMediaPlayer; 177 178 /* List of Media player instances, useful for retrieving MediaPlayerList or MediaPlayerInfo */ 179 private SortedMap<Integer, MediaPlayerInfo> mMediaPlayerInfoList; 180 private boolean mAvailablePlayerViewChanged; 181 182 /* List of media players which supports browse */ 183 private List<BrowsePlayerInfo> mBrowsePlayerInfoList; 184 185 /* Manage browsed players */ 186 private AvrcpBrowseManager mAvrcpBrowseManager; 187 188 /* Broadcast receiver for device connections intent broadcasts */ 189 private final BroadcastReceiver mAvrcpReceiver = new AvrcpServiceBroadcastReceiver(); 190 private final BroadcastReceiver mBootReceiver = new AvrcpServiceBootReceiver(); 191 192 /* Recording passthrough key dispatches */ 193 private static final int PASSTHROUGH_LOG_MAX_SIZE = DEBUG ? 50 : 10; 194 private EvictingQueue<MediaKeyLog> mPassthroughLogs; // Passthorugh keys dispatched 195 private List<MediaKeyLog> mPassthroughPending; // Passthrough keys sent not dispatched yet 196 private int mPassthroughDispatched; // Number of keys dispatched 197 198 private class MediaKeyLog { 199 private long mTimeSent; 200 private long mTimeProcessed; 201 private String mPackage; 202 private KeyEvent mEvent; 203 204 MediaKeyLog(long time, KeyEvent event) { 205 mEvent = event; 206 mTimeSent = time; 207 } 208 209 public boolean addDispatch(long time, KeyEvent event, String packageName) { 210 if (mPackage != null) { 211 return false; 212 } 213 if (event.getAction() != mEvent.getAction()) { 214 return false; 215 } 216 if (event.getKeyCode() != mEvent.getKeyCode()) { 217 return false; 218 } 219 mPackage = packageName; 220 mTimeProcessed = time; 221 return true; 222 } 223 224 @Override 225 public String toString() { 226 StringBuilder sb = new StringBuilder(); 227 sb.append(android.text.format.DateFormat.format("MM-dd HH:mm:ss", mTimeSent)); 228 sb.append(" " + mEvent.toString()); 229 if (mPackage == null) { 230 sb.append(" (undispatched)"); 231 } else { 232 sb.append(" to " + mPackage); 233 sb.append(" in " + (mTimeProcessed - mTimeSent) + "ms"); 234 } 235 return sb.toString(); 236 } 237 } 238 239 static { 240 classInitNative(); 241 } 242 243 private Avrcp(Context context) { 244 mMediaAttributes = new MediaAttributes(null); 245 mLastQueueId = MediaSession.QueueItem.UNKNOWN_ID; 246 mCurrentPlayState = 247 new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build(); 248 mReportedPlayStatus = PLAYSTATUS_ERROR; 249 mA2dpState = BluetoothA2dp.STATE_NOT_PLAYING; 250 mAudioManagerIsPlaying = false; 251 mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 252 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 253 mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 254 mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 255 mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 256 mPlaybackIntervalMs = 0L; 257 mLastReportedPosition = -1; 258 mNextPosMs = -1; 259 mPrevPosMs = -1; 260 mFeatures = 0; 261 mRemoteVolume = -1; 262 mInitialRemoteVolume = -1; 263 mLastRemoteVolume = -1; 264 mLastDirection = 0; 265 mVolCmdSetInProgress = false; 266 mAbsVolRetryTimes = 0; 267 mLocalVolume = -1; 268 mLastLocalVolume = -1; 269 mAbsVolThreshold = 0; 270 mVolumeMapping = new HashMap<Integer, Integer>(); 271 mCurrAddrPlayerID = NO_PLAYER_ID; 272 mReportedPlayerID = mCurrAddrPlayerID; 273 mCurrBrowsePlayerID = 0; 274 mContext = context; 275 mLastUsedPlayerID = 0; 276 mAddressedMediaPlayer = null; 277 278 initNative(); 279 280 mMediaSessionManager = 281 (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); 282 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 283 mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 284 mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL / mAudioStreamMax); 285 286 Resources resources = context.getResources(); 287 if (resources != null) { 288 mAbsVolThreshold = 289 resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold); 290 291 // Update the threshold if the thresholdPercent is valid 292 int thresholdPercent = 293 resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold_percent); 294 if (thresholdPercent >= 0 && thresholdPercent <= 100) { 295 mAbsVolThreshold = (thresholdPercent * mAudioStreamMax) / 100; 296 } 297 } 298 299 // Register for package removal intent broadcasts for media button receiver persistence 300 IntentFilter pkgFilter = new IntentFilter(); 301 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 302 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 303 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 304 pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); 305 pkgFilter.addDataScheme("package"); 306 context.registerReceiver(mAvrcpReceiver, pkgFilter); 307 308 IntentFilter bootFilter = new IntentFilter(); 309 bootFilter.addAction(Intent.ACTION_USER_UNLOCKED); 310 context.registerReceiver(mBootReceiver, bootFilter); 311 } 312 313 private synchronized void start() { 314 HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler"); 315 thread.start(); 316 Looper looper = thread.getLooper(); 317 mHandler = new AvrcpMessageHandler(looper); 318 mAudioManagerPlaybackHandler = new Handler(looper); 319 mAudioManagerPlaybackCb = new AudioManagerPlaybackListener(); 320 mMediaControllerCb = new MediaControllerListener(); 321 mAvrcpMediaRsp = new AvrcpMediaRsp(); 322 mMediaPlayerInfoList = new TreeMap<Integer, MediaPlayerInfo>(); 323 mAvailablePlayerViewChanged = false; 324 mBrowsePlayerInfoList = Collections.synchronizedList(new ArrayList<BrowsePlayerInfo>()); 325 mPassthroughDispatched = 0; 326 mPassthroughLogs = new EvictingQueue<MediaKeyLog>(PASSTHROUGH_LOG_MAX_SIZE); 327 mPassthroughPending = Collections.synchronizedList(new ArrayList<MediaKeyLog>()); 328 if (mMediaSessionManager != null) { 329 mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveSessionListener, null, 330 mHandler); 331 mMediaSessionManager.setCallback(mButtonDispatchCallback, null); 332 } 333 mPackageManager = mContext.getApplicationContext().getPackageManager(); 334 335 /* create object to communicate with addressed player */ 336 mAddressedMediaPlayer = new AddressedMediaPlayer(mAvrcpMediaRsp); 337 338 /* initialize BrowseMananger which manages Browse commands and response */ 339 mAvrcpBrowseManager = new AvrcpBrowseManager(mContext, mAvrcpMediaRsp); 340 341 initMediaPlayersList(); 342 343 UserManager manager = UserManager.get(mContext); 344 if (manager == null || manager.isUserUnlocked()) { 345 if (DEBUG) { 346 Log.d(TAG, "User already unlocked, initializing player lists"); 347 } 348 // initialize browsable player list and build media player list 349 buildBrowsablePlayerList(); 350 } 351 352 mAudioManager.registerAudioPlaybackCallback(mAudioManagerPlaybackCb, 353 mAudioManagerPlaybackHandler); 354 } 355 356 public static Avrcp make(Context context) { 357 if (DEBUG) { 358 Log.v(TAG, "make"); 359 } 360 Avrcp ar = new Avrcp(context); 361 ar.start(); 362 return ar; 363 } 364 365 public synchronized void doQuit() { 366 if (DEBUG) { 367 Log.d(TAG, "doQuit"); 368 } 369 if (mAudioManager != null) { 370 mAudioManager.unregisterAudioPlaybackCallback(mAudioManagerPlaybackCb); 371 } 372 if (mMediaController != null) { 373 mMediaController.unregisterCallback(mMediaControllerCb); 374 } 375 if (mMediaSessionManager != null) { 376 mMediaSessionManager.setCallback(null, null); 377 mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveSessionListener); 378 } 379 380 mAudioManagerPlaybackHandler.removeCallbacksAndMessages(null); 381 mHandler.removeCallbacksAndMessages(null); 382 Looper looper = mHandler.getLooper(); 383 mHandler = null; 384 if (looper != null) { 385 looper.quitSafely(); 386 } 387 388 mAudioManagerPlaybackHandler = null; 389 mContext.unregisterReceiver(mAvrcpReceiver); 390 mContext.unregisterReceiver(mBootReceiver); 391 392 mAddressedMediaPlayer.cleanup(); 393 mAvrcpBrowseManager.cleanup(); 394 } 395 396 public void cleanup() { 397 if (DEBUG) { 398 Log.d(TAG, "cleanup"); 399 } 400 cleanupNative(); 401 if (mVolumeMapping != null) { 402 mVolumeMapping.clear(); 403 } 404 } 405 406 private class AudioManagerPlaybackListener extends AudioManager.AudioPlaybackCallback { 407 @Override 408 public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) { 409 super.onPlaybackConfigChanged(configs); 410 boolean isPlaying = false; 411 for (AudioPlaybackConfiguration config : configs) { 412 if (DEBUG) { 413 Log.d(TAG, "AudioManager Player: " 414 + AudioPlaybackConfiguration.toLogFriendlyString(config)); 415 } 416 if (config.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { 417 isPlaying = true; 418 break; 419 } 420 } 421 if (DEBUG) { 422 Log.d(TAG, "AudioManager isPlaying: " + isPlaying); 423 } 424 if (mAudioManagerIsPlaying != isPlaying) { 425 mAudioManagerIsPlaying = isPlaying; 426 updateCurrentMediaState(); 427 } 428 } 429 } 430 431 private class MediaControllerListener extends MediaController.Callback { 432 @Override 433 public void onMetadataChanged(MediaMetadata metadata) { 434 if (DEBUG) { 435 Log.v(TAG, "onMetadataChanged"); 436 } 437 updateCurrentMediaState(); 438 } 439 440 @Override 441 public synchronized void onPlaybackStateChanged(PlaybackState state) { 442 if (DEBUG) { 443 Log.v(TAG, "onPlaybackStateChanged: state " + state.toString()); 444 } 445 446 updateCurrentMediaState(); 447 } 448 449 @Override 450 public void onSessionDestroyed() { 451 Log.v(TAG, "MediaController session destroyed"); 452 synchronized (Avrcp.this) { 453 if (mMediaController != null) { 454 removeMediaController(mMediaController.getWrappedInstance()); 455 } 456 } 457 } 458 459 @Override 460 public void onQueueChanged(List<MediaSession.QueueItem> queue) { 461 if (queue == null) { 462 Log.v(TAG, "onQueueChanged: received null queue"); 463 return; 464 } 465 466 final AvrcpMessageHandler handler = mHandler; 467 if (handler == null) { 468 if (DEBUG) Log.d(TAG, "onQueueChanged: mHandler is already null"); 469 return; 470 } 471 472 Log.v(TAG, "onQueueChanged: NowPlaying list changed, Queue Size = " 473 + queue.size()); 474 handler.sendEmptyMessage(MSG_NOW_PLAYING_CHANGED_RSP); 475 } 476 } 477 478 /** Handles Avrcp messages. */ 479 private final class AvrcpMessageHandler extends Handler { 480 private AvrcpMessageHandler(Looper looper) { 481 super(looper); 482 } 483 484 @Override 485 public void handleMessage(Message msg) { 486 switch (msg.what) { 487 case MSG_NATIVE_REQ_GET_RC_FEATURES: { 488 String address = (String) msg.obj; 489 mFeatures = msg.arg1; 490 mFeatures = modifyRcFeatureFromBlacklist(mFeatures, address); 491 if (DEBUG) { 492 Log.v(TAG, 493 "MSG_NATIVE_REQ_GET_RC_FEATURES: address=" + address + ", features=" 494 + msg.arg1 + ", mFeatures=" + mFeatures); 495 } 496 mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported()); 497 mLastLocalVolume = -1; 498 mRemoteVolume = -1; 499 mLocalVolume = -1; 500 mInitialRemoteVolume = -1; 501 mAddress = address; 502 if (mVolumeMapping != null) { 503 mVolumeMapping.clear(); 504 } 505 break; 506 } 507 508 case MSG_NATIVE_REQ_GET_PLAY_STATUS: { 509 byte[] address = (byte[]) msg.obj; 510 int btstate = getBluetoothPlayState(mCurrentPlayState); 511 int length = (int) mMediaAttributes.getLength(); 512 int position = (int) getPlayPosition(); 513 if (DEBUG) { 514 Log.v(TAG, 515 "MSG_NATIVE_REQ_GET_PLAY_STATUS, responding with state " + btstate 516 + " len " + length + " pos " + position); 517 } 518 getPlayStatusRspNative(address, btstate, length, position); 519 break; 520 } 521 522 case MSG_NATIVE_REQ_GET_ELEM_ATTRS: { 523 String[] textArray; 524 AvrcpCmd.ElementAttrCmd elem = (AvrcpCmd.ElementAttrCmd) msg.obj; 525 byte numAttr = elem.mNumAttr; 526 int[] attrIds = elem.mAttrIDs; 527 if (DEBUG) { 528 Log.v(TAG, "MSG_NATIVE_REQ_GET_ELEM_ATTRS:numAttr=" + numAttr); 529 } 530 textArray = new String[numAttr]; 531 StringBuilder responseDebug = new StringBuilder(); 532 responseDebug.append("getElementAttr response: "); 533 for (int i = 0; i < numAttr; ++i) { 534 textArray[i] = mMediaAttributes.getString(attrIds[i]); 535 responseDebug.append("[" + attrIds[i] + "="); 536 if (attrIds[i] == AvrcpConstants.ATTRID_TITLE 537 || attrIds[i] == AvrcpConstants.ATTRID_ARTIST 538 || attrIds[i] == AvrcpConstants.ATTRID_ALBUM) { 539 responseDebug.append(Utils.ellipsize(textArray[i]) + "] "); 540 } else { 541 responseDebug.append(textArray[i] + "] "); 542 } 543 } 544 Log.v(TAG, responseDebug.toString()); 545 byte[] bdaddr = elem.mAddress; 546 getElementAttrRspNative(bdaddr, numAttr, attrIds, textArray); 547 break; 548 } 549 550 case MSG_NATIVE_REQ_REGISTER_NOTIFICATION: 551 if (DEBUG) { 552 Log.v(TAG, 553 "MSG_NATIVE_REQ_REGISTER_NOTIFICATION:event=" + msg.arg1 + " param=" 554 + msg.arg2); 555 } 556 processRegisterNotification((byte[]) msg.obj, msg.arg1, msg.arg2); 557 break; 558 559 case MSG_NOW_PLAYING_CHANGED_RSP: 560 if (DEBUG) { 561 Log.v(TAG, "MSG_NOW_PLAYING_CHANGED_RSP"); 562 } 563 removeMessages(MSG_NOW_PLAYING_CHANGED_RSP); 564 updateCurrentMediaState(); 565 break; 566 567 case MSG_PLAY_INTERVAL_TIMEOUT: 568 sendPlayPosNotificationRsp(false); 569 break; 570 571 case MSG_NATIVE_REQ_VOLUME_CHANGE: 572 if (!isAbsoluteVolumeSupported()) { 573 if (DEBUG) { 574 Log.v(TAG, "MSG_NATIVE_REQ_VOLUME_CHANGE ignored, not supported"); 575 } 576 break; 577 } 578 byte absVol = (byte) ((byte) msg.arg1 & 0x7f); // discard MSB as it is RFD 579 if (DEBUG) { 580 Log.v(TAG, "MSG_NATIVE_REQ_VOLUME_CHANGE: volume=" + absVol + " ctype=" 581 + msg.arg2); 582 } 583 584 if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) { 585 if (!mVolCmdSetInProgress) { 586 Log.e(TAG, "Unsolicited response, ignored"); 587 break; 588 } 589 removeMessages(MSG_ABS_VOL_TIMEOUT); 590 591 mVolCmdSetInProgress = false; 592 mAbsVolRetryTimes = 0; 593 } 594 595 // convert remote volume to local volume 596 int volIndex = convertToAudioStreamVolume(absVol); 597 if (mInitialRemoteVolume == -1) { 598 mInitialRemoteVolume = absVol; 599 if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax 600 && volIndex > mAbsVolThreshold) { 601 if (DEBUG) { 602 Log.v(TAG, "remote inital volume too high " + volIndex + ">" 603 + mAbsVolThreshold); 604 } 605 Message msg1 = this.obtainMessage(MSG_SET_ABSOLUTE_VOLUME, 606 mAbsVolThreshold, 0); 607 this.sendMessage(msg1); 608 mRemoteVolume = absVol; 609 mLocalVolume = volIndex; 610 break; 611 } 612 } 613 614 if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT 615 || msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_INTERIM)) { 616 /* If the volume has successfully changed */ 617 mLocalVolume = volIndex; 618 if (mLastLocalVolume != -1 && msg.arg2 == AVRC_RSP_ACCEPT) { 619 if (mLastLocalVolume != volIndex) { 620 /* remote volume changed more than requested due to 621 * local and remote has different volume steps */ 622 if (DEBUG) { 623 Log.d(TAG, 624 "Remote returned volume does not match desired volume " 625 + mLastLocalVolume + " vs " + volIndex); 626 } 627 mLastLocalVolume = mLocalVolume; 628 } 629 } 630 631 notifyVolumeChanged(mLocalVolume); 632 mRemoteVolume = absVol; 633 long pecentVolChanged = ((long) absVol * 100) / 0x7f; 634 Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%"); 635 } else if (msg.arg2 == AVRC_RSP_REJ) { 636 Log.e(TAG, "setAbsoluteVolume call rejected"); 637 } 638 break; 639 640 case MSG_SET_ABSOLUTE_VOLUME: 641 if (!isAbsoluteVolumeSupported()) { 642 if (DEBUG) { 643 Log.v(TAG, "ignore MSG_SET_ABSOLUTE_VOLUME"); 644 } 645 break; 646 } 647 648 if (DEBUG) { 649 Log.v(TAG, "MSG_SET_ABSOLUTE_VOLUME"); 650 } 651 652 if (mVolCmdSetInProgress) { 653 if (DEBUG) { 654 Log.w(TAG, "There is already a volume command in progress."); 655 } 656 break; 657 } 658 659 // Remote device didn't set initial volume. Let's black list it 660 if (mInitialRemoteVolume == -1) { 661 if (DEBUG) { 662 Log.d(TAG, "remote " + mAddress 663 + " never tell us initial volume, black list it."); 664 } 665 blackListCurrentDevice("MSG_SET_ABSOLUTE_VOLUME"); 666 break; 667 } 668 669 int avrcpVolume = convertToAvrcpVolume(msg.arg1); 670 avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume)); 671 if (DEBUG) { 672 Log.d(TAG, "Setting volume to " + msg.arg1 + "-" + avrcpVolume); 673 } 674 if (setVolumeNative(avrcpVolume)) { 675 sendMessageDelayed(obtainMessage(MSG_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY); 676 mVolCmdSetInProgress = true; 677 mLastRemoteVolume = avrcpVolume; 678 mLastLocalVolume = msg.arg1; 679 } else { 680 if (DEBUG) { 681 Log.d(TAG, "setVolumeNative failed"); 682 } 683 } 684 break; 685 686 case MSG_ABS_VOL_TIMEOUT: 687 if (DEBUG) { 688 Log.v(TAG, "MSG_ABS_VOL_TIMEOUT: Volume change cmd timed out."); 689 } 690 mVolCmdSetInProgress = false; 691 if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) { 692 mAbsVolRetryTimes = 0; 693 /* too many volume change failures, black list the device */ 694 blackListCurrentDevice("MSG_ABS_VOL_TIMEOUT"); 695 } else { 696 mAbsVolRetryTimes += 1; 697 if (setVolumeNative(mLastRemoteVolume)) { 698 sendMessageDelayed(obtainMessage(MSG_ABS_VOL_TIMEOUT), 699 CMD_TIMEOUT_DELAY); 700 mVolCmdSetInProgress = true; 701 } 702 } 703 break; 704 705 case MSG_SET_A2DP_AUDIO_STATE: 706 if (DEBUG) { 707 Log.v(TAG, "MSG_SET_A2DP_AUDIO_STATE:" + msg.arg1); 708 } 709 mA2dpState = msg.arg1; 710 updateCurrentMediaState(); 711 break; 712 713 case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: { 714 AvrcpCmd.FolderItemsCmd folderObj = (AvrcpCmd.FolderItemsCmd) msg.obj; 715 if (DEBUG) { 716 Log.v(TAG, "MSG_NATIVE_REQ_GET_FOLDER_ITEMS " + folderObj); 717 } 718 switch (folderObj.mScope) { 719 case AvrcpConstants.BTRC_SCOPE_PLAYER_LIST: 720 handleMediaPlayerListRsp(folderObj); 721 break; 722 case AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM: 723 case AvrcpConstants.BTRC_SCOPE_NOW_PLAYING: 724 handleGetFolderItemBrowseResponse(folderObj, folderObj.mAddress); 725 break; 726 default: 727 Log.e(TAG, "unknown scope for getfolderitems. scope = " 728 + folderObj.mScope); 729 getFolderItemsRspNative(folderObj.mAddress, 730 AvrcpConstants.RSP_INV_SCOPE, (short) 0, (byte) 0, 0, null, 731 null, null, null, null, null, null, null); 732 } 733 break; 734 } 735 736 case MSG_NATIVE_REQ_SET_ADDR_PLAYER: 737 // object is bdaddr, argument 1 is the selected player id 738 if (DEBUG) { 739 Log.v(TAG, "MSG_NATIVE_REQ_SET_ADDR_PLAYER id=" + msg.arg1); 740 } 741 setAddressedPlayer((byte[]) msg.obj, msg.arg1); 742 break; 743 744 case MSG_NATIVE_REQ_GET_ITEM_ATTR: 745 // msg object contains the item attribute object 746 AvrcpCmd.ItemAttrCmd cmd = (AvrcpCmd.ItemAttrCmd) msg.obj; 747 if (DEBUG) { 748 Log.v(TAG, "MSG_NATIVE_REQ_GET_ITEM_ATTR " + cmd); 749 } 750 handleGetItemAttr(cmd); 751 break; 752 753 case MSG_NATIVE_REQ_SET_BR_PLAYER: 754 // argument 1 is the selected player id 755 if (DEBUG) { 756 Log.v(TAG, "MSG_NATIVE_REQ_SET_BR_PLAYER id=" + msg.arg1); 757 } 758 setBrowsedPlayer((byte[]) msg.obj, msg.arg1); 759 break; 760 761 case MSG_NATIVE_REQ_CHANGE_PATH: { 762 if (DEBUG) { 763 Log.v(TAG, "MSG_NATIVE_REQ_CHANGE_PATH"); 764 } 765 Bundle data = msg.getData(); 766 byte[] bdaddr = data.getByteArray("BdAddress"); 767 byte[] folderUid = data.getByteArray("folderUid"); 768 byte direction = data.getByte("direction"); 769 if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) { 770 mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) 771 .changePath(folderUid, direction); 772 } else { 773 Log.e(TAG, "Remote requesting change path before setbrowsedplayer"); 774 changePathRspNative(bdaddr, AvrcpConstants.RSP_BAD_CMD, 0); 775 } 776 break; 777 } 778 779 case MSG_NATIVE_REQ_PLAY_ITEM: { 780 Bundle data = msg.getData(); 781 byte[] bdaddr = data.getByteArray("BdAddress"); 782 byte[] uid = data.getByteArray("uid"); 783 byte scope = data.getByte("scope"); 784 if (DEBUG) { 785 Log.v(TAG, "MSG_NATIVE_REQ_PLAY_ITEM scope=" + scope + " id=" 786 + Utils.byteArrayToString(uid)); 787 } 788 handlePlayItemResponse(bdaddr, uid, scope); 789 break; 790 } 791 792 case MSG_NATIVE_REQ_GET_TOTAL_NUM_OF_ITEMS: 793 if (DEBUG) { 794 Log.v(TAG, "MSG_NATIVE_REQ_GET_TOTAL_NUM_OF_ITEMS scope=" + msg.arg1); 795 } 796 // argument 1 is scope, object is bdaddr 797 handleGetTotalNumOfItemsResponse((byte[]) msg.obj, (byte) msg.arg1); 798 break; 799 800 case MSG_NATIVE_REQ_PASS_THROUGH: 801 if (DEBUG) { 802 Log.v(TAG, 803 "MSG_NATIVE_REQ_PASS_THROUGH: id=" + msg.arg1 + " st=" + msg.arg2); 804 } 805 // argument 1 is id, argument 2 is keyState 806 handlePassthroughCmd(msg.arg1, msg.arg2); 807 break; 808 809 default: 810 Log.e(TAG, "unknown message! msg.what=" + msg.what); 811 break; 812 } 813 } 814 } 815 816 private PlaybackState updatePlaybackState() { 817 PlaybackState newState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, 818 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build(); 819 synchronized (this) { 820 PlaybackState controllerState = null; 821 if (mMediaController != null) { 822 controllerState = mMediaController.getPlaybackState(); 823 } 824 825 if (controllerState != null) { 826 newState = controllerState; 827 } 828 // Use the AudioManager to update the playback state. 829 // NOTE: We cannot use the 830 // (mA2dpState == BluetoothA2dp.STATE_PLAYING) 831 // check, because after Pause, the A2DP state remains in 832 // STATE_PLAYING for 3 more seconds. 833 // As a result of that, if we pause the music, on carkits the 834 // Play status indicator will continue to display "Playing" 835 // for 3 more seconds which can be confusing. 836 if ((mAudioManagerIsPlaying && newState.getState() != PlaybackState.STATE_PLAYING) || ( 837 controllerState == null && mAudioManager != null 838 && mAudioManager.isMusicActive())) { 839 // Use AudioManager playback state if we don't have the state 840 // from MediaControlller 841 PlaybackState.Builder builder = new PlaybackState.Builder(); 842 if (mAudioManagerIsPlaying) { 843 builder.setState(PlaybackState.STATE_PLAYING, 844 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f); 845 } else { 846 builder.setState(PlaybackState.STATE_PAUSED, 847 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f); 848 } 849 newState = builder.build(); 850 } 851 } 852 853 byte newPlayStatus = getBluetoothPlayState(newState); 854 855 /* update play status in global media player list */ 856 MediaPlayerInfo player = getAddressedPlayerInfo(); 857 if (player != null) { 858 player.setPlayStatus(newPlayStatus); 859 } 860 861 if (DEBUG) { 862 Log.v(TAG, "updatePlaybackState (" + mPlayStatusChangedNT + "): " + mReportedPlayStatus 863 + "➡" + newPlayStatus + "(" + newState + ")"); 864 } 865 866 if (newState != null) { 867 mCurrentPlayState = newState; 868 } 869 870 return mCurrentPlayState; 871 } 872 873 private void sendPlaybackStatus(int playStatusChangedNT, byte playbackState) { 874 registerNotificationRspPlayStatusNative(playStatusChangedNT, playbackState); 875 mPlayStatusChangedNT = playStatusChangedNT; 876 mReportedPlayStatus = playbackState; 877 } 878 879 private void updateTransportControls(int transportControlFlags) { 880 mTransportControlFlags = transportControlFlags; 881 } 882 883 class MediaAttributes { 884 private boolean mExists; 885 private String mTitle; 886 private String mArtistName; 887 private String mAlbumName; 888 private String mMediaNumber; 889 private String mMediaTotalNumber; 890 private String mGenre; 891 private long mPlayingTimeMs; 892 893 private static final int ATTR_TITLE = 1; 894 private static final int ATTR_ARTIST_NAME = 2; 895 private static final int ATTR_ALBUM_NAME = 3; 896 private static final int ATTR_MEDIA_NUMBER = 4; 897 private static final int ATTR_MEDIA_TOTAL_NUMBER = 5; 898 private static final int ATTR_GENRE = 6; 899 private static final int ATTR_PLAYING_TIME_MS = 7; 900 901 902 MediaAttributes(MediaMetadata data) { 903 mExists = data != null; 904 if (!mExists) { 905 return; 906 } 907 908 mArtistName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ARTIST)); 909 mAlbumName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ALBUM)); 910 mMediaNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER)); 911 mMediaTotalNumber = 912 longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)); 913 mGenre = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_GENRE)); 914 mPlayingTimeMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION); 915 916 // Try harder for the title. 917 mTitle = data.getString(MediaMetadata.METADATA_KEY_TITLE); 918 919 if (mTitle == null) { 920 MediaDescription desc = data.getDescription(); 921 if (desc != null) { 922 CharSequence val = desc.getDescription(); 923 if (val != null) { 924 mTitle = val.toString(); 925 } 926 } 927 } 928 929 if (mTitle == null) { 930 mTitle = new String(); 931 } 932 } 933 934 public long getLength() { 935 if (!mExists) { 936 return 0L; 937 } 938 return mPlayingTimeMs; 939 } 940 941 public boolean equals(MediaAttributes other) { 942 if (other == null) { 943 return false; 944 } 945 946 if (mExists != other.mExists) { 947 return false; 948 } 949 950 if (!mExists) { 951 return true; 952 } 953 954 return (mTitle.equals(other.mTitle)) && (mArtistName.equals(other.mArtistName)) 955 && (mAlbumName.equals(other.mAlbumName)) && (mMediaNumber.equals( 956 other.mMediaNumber)) && (mMediaTotalNumber.equals(other.mMediaTotalNumber)) 957 && (mGenre.equals(other.mGenre)) && (mPlayingTimeMs == other.mPlayingTimeMs); 958 } 959 960 public String getString(int attrId) { 961 if (!mExists) { 962 return new String(); 963 } 964 965 switch (attrId) { 966 case ATTR_TITLE: 967 return mTitle; 968 case ATTR_ARTIST_NAME: 969 return mArtistName; 970 case ATTR_ALBUM_NAME: 971 return mAlbumName; 972 case ATTR_MEDIA_NUMBER: 973 return mMediaNumber; 974 case ATTR_MEDIA_TOTAL_NUMBER: 975 return mMediaTotalNumber; 976 case ATTR_GENRE: 977 return mGenre; 978 case ATTR_PLAYING_TIME_MS: 979 return Long.toString(mPlayingTimeMs); 980 default: 981 return new String(); 982 } 983 } 984 985 private String stringOrBlank(String s) { 986 return s == null ? new String() : s; 987 } 988 989 private String longStringOrBlank(Long s) { 990 return s == null ? new String() : s.toString(); 991 } 992 993 @Override 994 public String toString() { 995 if (!mExists) { 996 return "[MediaAttributes: none]"; 997 } 998 999 return "[MediaAttributes: " + mTitle + " - " + mAlbumName + " by " + mArtistName + " (" 1000 + mPlayingTimeMs + " " + mMediaNumber + "/" + mMediaTotalNumber + ") " + mGenre 1001 + "]"; 1002 } 1003 1004 public String toRedactedString() { 1005 if (!mExists) { 1006 return "[MediaAttributes: none]"; 1007 } 1008 1009 return "[MediaAttributes: " + Utils.ellipsize(mTitle) + " - " + Utils.ellipsize( 1010 mAlbumName) + " by " + Utils.ellipsize(mArtistName) + " (" + mPlayingTimeMs 1011 + " " + mMediaNumber + "/" + mMediaTotalNumber + ") " + mGenre + "]"; 1012 } 1013 } 1014 1015 private void updateCurrentMediaState() { 1016 // Only do player updates when we aren't registering for track changes. 1017 MediaAttributes currentAttributes; 1018 PlaybackState newState = updatePlaybackState(); 1019 1020 synchronized (this) { 1021 if (mMediaController == null) { 1022 currentAttributes = new MediaAttributes(null); 1023 } else { 1024 currentAttributes = new MediaAttributes(mMediaController.getMetadata()); 1025 } 1026 } 1027 1028 byte newPlayStatus = getBluetoothPlayState(newState); 1029 1030 if (newState.getState() != PlaybackState.STATE_BUFFERING 1031 && newState.getState() != PlaybackState.STATE_NONE) { 1032 long newQueueId = MediaSession.QueueItem.UNKNOWN_ID; 1033 if (newState != null) { 1034 newQueueId = newState.getActiveQueueItemId(); 1035 } 1036 if (DEBUG) { 1037 Log.v(TAG, 1038 "Media update: id " + mLastQueueId + "➡" + newQueueId + "? " + currentAttributes 1039 .toRedactedString() + " : " + mMediaAttributes.toRedactedString()); 1040 } 1041 1042 if (mAvailablePlayerViewChanged) { 1043 registerNotificationRspAvalPlayerChangedNative( 1044 AvrcpConstants.NOTIFICATION_TYPE_CHANGED); 1045 mAvailablePlayerViewChanged = false; 1046 return; 1047 } 1048 1049 if (mAddrPlayerChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM 1050 && mReportedPlayerID != mCurrAddrPlayerID) { 1051 registerNotificationRspAvalPlayerChangedNative( 1052 AvrcpConstants.NOTIFICATION_TYPE_CHANGED); 1053 registerNotificationRspAddrPlayerChangedNative( 1054 AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID, sUIDCounter); 1055 1056 mAvailablePlayerViewChanged = false; 1057 mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 1058 mReportedPlayerID = mCurrAddrPlayerID; 1059 1060 // Update the now playing list without sending the notification 1061 mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 1062 mAddressedMediaPlayer.updateNowPlayingList(mMediaController); 1063 mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM; 1064 } 1065 1066 // Dont send now playing list changed if the player doesn't support browsing 1067 MediaPlayerInfo info = getAddressedPlayerInfo(); 1068 if (info != null && info.isBrowseSupported()) { 1069 if (DEBUG) { 1070 Log.v(TAG, "Check if NowPlayingList is updated"); 1071 } 1072 mAddressedMediaPlayer.updateNowPlayingList(mMediaController); 1073 } 1074 1075 // Notify track changed if: 1076 // - The CT is registered for the notification 1077 // - Queue ID is UNKNOWN and MediaMetadata is different 1078 // - Queue ID is valid and different from last Queue ID sent 1079 if ((newQueueId == -1 || newQueueId != mLastQueueId) 1080 && mTrackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM 1081 && !currentAttributes.equals(mMediaAttributes) 1082 && newPlayStatus == PLAYSTATUS_PLAYING) { 1083 Log.v(TAG, "Send track changed"); 1084 mMediaAttributes = currentAttributes; 1085 mLastQueueId = newQueueId; 1086 sendTrackChangedRsp(false); 1087 } 1088 } else { 1089 Log.i(TAG, "Skipping update due to invalid playback state"); 1090 } 1091 1092 // still send the updated play state if the playback state is none or buffering 1093 if (DEBUG) { 1094 Log.v(TAG, "play status change " + mReportedPlayStatus + "➡" + newPlayStatus 1095 + " mPlayStatusChangedNT: " + mPlayStatusChangedNT); 1096 } 1097 if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM || (mReportedPlayStatus 1098 != newPlayStatus)) { 1099 sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newPlayStatus); 1100 } 1101 1102 sendPlayPosNotificationRsp(false); 1103 } 1104 1105 private void getRcFeaturesRequestFromNative(byte[] address, int features) { 1106 final AvrcpMessageHandler handler = mHandler; 1107 if (handler == null) { 1108 if (DEBUG) Log.d(TAG, "getRcFeaturesRequestFromNative: mHandler is already null"); 1109 return; 1110 } 1111 1112 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_GET_RC_FEATURES, features, 0, 1113 Utils.getAddressStringFromByte(address)); 1114 handler.sendMessage(msg); 1115 } 1116 1117 private void getPlayStatusRequestFromNative(byte[] address) { 1118 final AvrcpMessageHandler handler = mHandler; 1119 if (handler == null) { 1120 if (DEBUG) Log.d(TAG, "getPlayStatusRequestFromNative: mHandler is already null"); 1121 return; 1122 } 1123 1124 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_GET_PLAY_STATUS); 1125 msg.obj = address; 1126 handler.sendMessage(msg); 1127 } 1128 1129 private void getElementAttrRequestFromNative(byte[] address, byte numAttr, int[] attrs) { 1130 AvrcpCmd avrcpCmdobj = new AvrcpCmd(); 1131 AvrcpCmd.ElementAttrCmd elemAttr = avrcpCmdobj.new ElementAttrCmd(address, numAttr, attrs); 1132 Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_ELEM_ATTRS); 1133 msg.obj = elemAttr; 1134 mHandler.sendMessage(msg); 1135 } 1136 1137 private void registerNotificationRequestFromNative(byte[] address, int eventId, int param) { 1138 final AvrcpMessageHandler handler = mHandler; 1139 if (handler == null) { 1140 if (DEBUG) { 1141 Log.d(TAG, "registerNotificationRequestFromNative: mHandler is already null"); 1142 } 1143 return; 1144 } 1145 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_REGISTER_NOTIFICATION, eventId, param); 1146 msg.obj = address; 1147 handler.sendMessage(msg); 1148 } 1149 1150 private void processRegisterNotification(byte[] address, int eventId, int param) { 1151 switch (eventId) { 1152 case EVT_PLAY_STATUS_CHANGED: 1153 mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 1154 updatePlaybackState(); 1155 sendPlaybackStatus(AvrcpConstants.NOTIFICATION_TYPE_INTERIM, mReportedPlayStatus); 1156 break; 1157 1158 case EVT_TRACK_CHANGED: 1159 Log.v(TAG, "Track changed notification enabled"); 1160 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM; 1161 sendTrackChangedRsp(true); 1162 break; 1163 1164 case EVT_PLAY_POS_CHANGED: 1165 mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM; 1166 mPlaybackIntervalMs = (long) param * 1000L; 1167 sendPlayPosNotificationRsp(true); 1168 break; 1169 1170 case EVT_AVBL_PLAYERS_CHANGED: 1171 /* Notify remote available players changed */ 1172 if (DEBUG) { 1173 Log.d(TAG, "Available Players notification enabled"); 1174 } 1175 registerNotificationRspAvalPlayerChangedNative( 1176 AvrcpConstants.NOTIFICATION_TYPE_INTERIM); 1177 break; 1178 1179 case EVT_ADDR_PLAYER_CHANGED: 1180 /* Notify remote addressed players changed */ 1181 if (DEBUG) { 1182 Log.d(TAG, "Addressed Player notification enabled"); 1183 } 1184 registerNotificationRspAddrPlayerChangedNative( 1185 AvrcpConstants.NOTIFICATION_TYPE_INTERIM, mCurrAddrPlayerID, sUIDCounter); 1186 mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM; 1187 mReportedPlayerID = mCurrAddrPlayerID; 1188 break; 1189 1190 case EVENT_UIDS_CHANGED: 1191 if (DEBUG) { 1192 Log.d(TAG, "UIDs changed notification enabled"); 1193 } 1194 registerNotificationRspUIDsChangedNative(AvrcpConstants.NOTIFICATION_TYPE_INTERIM, 1195 sUIDCounter); 1196 break; 1197 1198 case EVENT_NOW_PLAYING_CONTENT_CHANGED: 1199 if (DEBUG) { 1200 Log.d(TAG, "Now Playing List changed notification enabled"); 1201 } 1202 /* send interim response to remote device */ 1203 mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM; 1204 if (!registerNotificationRspNowPlayingChangedNative( 1205 AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) { 1206 Log.e(TAG, "EVENT_NOW_PLAYING_CONTENT_CHANGED: " 1207 + "registerNotificationRspNowPlayingChangedNative for Interim rsp " 1208 + "failed!"); 1209 } 1210 break; 1211 } 1212 } 1213 1214 private void handlePassthroughCmdRequestFromNative(byte[] address, int id, int keyState) { 1215 final AvrcpMessageHandler handler = mHandler; 1216 if (handler == null) { 1217 if (DEBUG) { 1218 Log.d(TAG, "handlePassthroughCmdRequestFromNative: mHandler is already null"); 1219 } 1220 return; 1221 } 1222 1223 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_PASS_THROUGH, id, keyState); 1224 handler.sendMessage(msg); 1225 } 1226 1227 private void sendTrackChangedRsp(boolean registering) { 1228 if (!registering && mTrackChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) { 1229 if (DEBUG) { 1230 Log.d(TAG, "sendTrackChangedRsp: Not registered or registering."); 1231 } 1232 return; 1233 } 1234 1235 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 1236 if (registering) { 1237 mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM; 1238 } 1239 1240 MediaPlayerInfo info = getAddressedPlayerInfo(); 1241 // for non-browsable players or no player 1242 if (info != null && !info.isBrowseSupported()) { 1243 byte[] track = AvrcpConstants.TRACK_IS_SELECTED; 1244 if (!mMediaAttributes.mExists) { 1245 track = AvrcpConstants.NO_TRACK_SELECTED; 1246 } 1247 registerNotificationRspTrackChangeNative(mTrackChangedNT, track); 1248 return; 1249 } 1250 1251 mAddressedMediaPlayer.sendTrackChangeWithId(mTrackChangedNT, mMediaController); 1252 } 1253 1254 private long getPlayPosition() { 1255 if (mCurrentPlayState == null) { 1256 return -1L; 1257 } 1258 1259 if (mCurrentPlayState.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) { 1260 return -1L; 1261 } 1262 1263 if (isPlayingState(mCurrentPlayState)) { 1264 long sinceUpdate = 1265 (SystemClock.elapsedRealtime() - mCurrentPlayState.getLastPositionUpdateTime()); 1266 return sinceUpdate + mCurrentPlayState.getPosition(); 1267 } 1268 1269 return mCurrentPlayState.getPosition(); 1270 } 1271 1272 private boolean isPlayingState(@Nullable PlaybackState state) { 1273 if (state == null) { 1274 return false; 1275 } 1276 return (state != null) && (state.getState() == PlaybackState.STATE_PLAYING); 1277 } 1278 1279 /** 1280 * Sends a play position notification, or schedules one to be 1281 * sent later at an appropriate time. If |requested| is true, 1282 * does both because this was called in reponse to a request from the 1283 * TG. 1284 */ 1285 private void sendPlayPosNotificationRsp(boolean requested) { 1286 if (!requested && mPlayPosChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) { 1287 if (DEBUG) { 1288 Log.d(TAG, "sendPlayPosNotificationRsp: Not registered or requesting."); 1289 } 1290 return; 1291 } 1292 1293 final AvrcpMessageHandler handler = mHandler; 1294 if (handler == null) { 1295 if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: handler is already null"); 1296 return; 1297 } 1298 1299 long playPositionMs = getPlayPosition(); 1300 String debugLine = "sendPlayPosNotificationRsp: "; 1301 1302 // mNextPosMs is set to -1 when the previous position was invalid 1303 // so this will be true if the new position is valid & old was invalid. 1304 // mPlayPositionMs is set to -1 when the new position is invalid, 1305 // and the old mPrevPosMs is >= 0 so this is true when the new is invalid 1306 // and the old was valid. 1307 if (DEBUG) { 1308 debugLine += "(" + requested + ") " + mPrevPosMs + " <=? " + playPositionMs + " <=? " 1309 + mNextPosMs; 1310 if (isPlayingState(mCurrentPlayState)) { 1311 debugLine += " Playing"; 1312 } 1313 debugLine += " State: " + mCurrentPlayState.getState(); 1314 } 1315 if (requested || ( 1316 (mLastReportedPosition != playPositionMs) && (playPositionMs >= mNextPosMs) || ( 1317 playPositionMs <= mPrevPosMs))) { 1318 if (!requested) { 1319 mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 1320 } 1321 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int) playPositionMs); 1322 mLastReportedPosition = playPositionMs; 1323 if (playPositionMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) { 1324 mNextPosMs = playPositionMs + mPlaybackIntervalMs; 1325 mPrevPosMs = playPositionMs - mPlaybackIntervalMs; 1326 } else { 1327 mNextPosMs = -1; 1328 mPrevPosMs = -1; 1329 } 1330 } 1331 1332 handler.removeMessages(MSG_PLAY_INTERVAL_TIMEOUT); 1333 if (mPlayPosChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM && isPlayingState( 1334 mCurrentPlayState)) { 1335 Message msg = handler.obtainMessage(MSG_PLAY_INTERVAL_TIMEOUT); 1336 long delay = mPlaybackIntervalMs; 1337 if (mNextPosMs != -1) { 1338 delay = mNextPosMs - (playPositionMs > 0 ? playPositionMs : 0); 1339 } 1340 if (DEBUG) { 1341 debugLine += " Timeout " + delay + "ms"; 1342 } 1343 handler.sendMessageDelayed(msg, delay); 1344 } 1345 if (DEBUG) { 1346 Log.d(TAG, debugLine); 1347 } 1348 } 1349 1350 /** 1351 * This is called from AudioService. It will return whether this device supports abs volume. 1352 * NOT USED AT THE MOMENT. 1353 */ 1354 public boolean isAbsoluteVolumeSupported() { 1355 return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0); 1356 } 1357 1358 /** 1359 * We get this call from AudioService. This will send a message to our handler object, 1360 * requesting our handler to call setVolumeNative() 1361 */ 1362 public void setAbsoluteVolume(int volume) { 1363 if (volume == mLocalVolume) { 1364 if (DEBUG) { 1365 Log.v(TAG, "setAbsoluteVolume is setting same index, ignore " + volume); 1366 } 1367 return; 1368 } 1369 1370 final AvrcpMessageHandler handler = mHandler; 1371 if (handler == null) { 1372 if (DEBUG) Log.d(TAG, "setAbsoluteVolume: mHandler is already null"); 1373 return; 1374 } 1375 1376 Message msg = handler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME, volume, 0); 1377 handler.sendMessage(msg); 1378 } 1379 1380 /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the 1381 * case when the volume is change locally on the carkit. This notification is not called when 1382 * the volume is changed from the phone. 1383 * 1384 * This method will send a message to our handler to change the local stored volume and notify 1385 * AudioService to update the UI 1386 */ 1387 private void volumeChangeRequestFromNative(byte[] address, int volume, int ctype) { 1388 final AvrcpMessageHandler handler = mHandler; 1389 if (handler == null) { 1390 if (DEBUG) Log.d(TAG, "volumeChangeRequestFromNative: mHandler is already null"); 1391 return; 1392 } 1393 1394 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_VOLUME_CHANGE, volume, ctype); 1395 Bundle data = new Bundle(); 1396 data.putByteArray("BdAddress", address); 1397 msg.setData(data); 1398 handler.sendMessage(msg); 1399 } 1400 1401 private void getFolderItemsRequestFromNative(byte[] address, byte scope, long startItem, 1402 long endItem, byte numAttr, int[] attrIds) { 1403 final AvrcpMessageHandler handler = mHandler; 1404 if (handler == null) { 1405 if (DEBUG) Log.d(TAG, "getFolderItemsRequestFromNative: mHandler is already null"); 1406 return; 1407 } 1408 AvrcpCmd avrcpCmdobj = new AvrcpCmd(); 1409 AvrcpCmd.FolderItemsCmd folderObj = 1410 avrcpCmdobj.new FolderItemsCmd(address, scope, startItem, endItem, numAttr, 1411 attrIds); 1412 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_GET_FOLDER_ITEMS, 0, 0); 1413 msg.obj = folderObj; 1414 handler.sendMessage(msg); 1415 } 1416 1417 private void setAddressedPlayerRequestFromNative(byte[] address, int playerId) { 1418 final AvrcpMessageHandler handler = mHandler; 1419 if (handler == null) { 1420 if (DEBUG) Log.d(TAG, "setAddressedPlayerRequestFromNative: mHandler is already null"); 1421 return; 1422 } 1423 1424 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_SET_ADDR_PLAYER, playerId, 0); 1425 msg.obj = address; 1426 handler.sendMessage(msg); 1427 } 1428 1429 private void setBrowsedPlayerRequestFromNative(byte[] address, int playerId) { 1430 final AvrcpMessageHandler handler = mHandler; 1431 if (handler == null) { 1432 if (DEBUG) Log.d(TAG, "setBrowsedPlayerRequestFromNative: mHandler is already null"); 1433 return; 1434 } 1435 1436 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_SET_BR_PLAYER, playerId, 0); 1437 msg.obj = address; 1438 handler.sendMessage(msg); 1439 } 1440 1441 private void changePathRequestFromNative(byte[] address, byte direction, byte[] folderUid) { 1442 final AvrcpMessageHandler handler = mHandler; 1443 if (handler == null) { 1444 if (DEBUG) Log.d(TAG, "changePathRequestFromNative: mHandler is already null"); 1445 return; 1446 } 1447 1448 Bundle data = new Bundle(); 1449 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_CHANGE_PATH); 1450 data.putByteArray("BdAddress", address); 1451 data.putByteArray("folderUid", folderUid); 1452 data.putByte("direction", direction); 1453 msg.setData(data); 1454 handler.sendMessage(msg); 1455 } 1456 1457 private void getItemAttrRequestFromNative(byte[] address, byte scope, byte[] itemUid, 1458 int uidCounter, byte numAttr, int[] attrs) { 1459 final AvrcpMessageHandler handler = mHandler; 1460 if (handler == null) { 1461 if (DEBUG) Log.d(TAG, "getItemAttrRequestFromNative: mHandler is already null"); 1462 return; 1463 } 1464 AvrcpCmd avrcpCmdobj = new AvrcpCmd(); 1465 AvrcpCmd.ItemAttrCmd itemAttr = 1466 avrcpCmdobj.new ItemAttrCmd(address, scope, itemUid, uidCounter, numAttr, attrs); 1467 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_GET_ITEM_ATTR); 1468 msg.obj = itemAttr; 1469 handler.sendMessage(msg); 1470 } 1471 1472 private void searchRequestFromNative(byte[] address, int charsetId, byte[] searchStr) { 1473 /* Search is not supported */ 1474 Log.w(TAG, "searchRequestFromNative: search is not supported"); 1475 searchRspNative(address, AvrcpConstants.RSP_SRCH_NOT_SPRTD, 0, 0); 1476 } 1477 1478 private void playItemRequestFromNative(byte[] address, byte scope, int uidCounter, byte[] uid) { 1479 final AvrcpMessageHandler handler = mHandler; 1480 if (handler == null) { 1481 if (DEBUG) Log.d(TAG, "playItemRequestFromNative: mHandler is already null"); 1482 return; 1483 } 1484 1485 Bundle data = new Bundle(); 1486 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_PLAY_ITEM); 1487 data.putByteArray("BdAddress", address); 1488 data.putByteArray("uid", uid); 1489 data.putInt("uidCounter", uidCounter); 1490 data.putByte("scope", scope); 1491 1492 msg.setData(data); 1493 handler.sendMessage(msg); 1494 } 1495 1496 private void addToPlayListRequestFromNative(byte[] address, byte scope, byte[] uid, 1497 int uidCounter) { 1498 /* add to NowPlaying not supported */ 1499 Log.w(TAG, "addToPlayListRequestFromNative: not supported! scope=" + scope); 1500 addToNowPlayingRspNative(address, AvrcpConstants.RSP_INTERNAL_ERR); 1501 } 1502 1503 private void getTotalNumOfItemsRequestFromNative(byte[] address, byte scope) { 1504 final AvrcpMessageHandler handler = mHandler; 1505 if (handler == null) { 1506 if (DEBUG) Log.d(TAG, "getTotalNumOfItemsRequestFromNative: mHandler is already null"); 1507 return; 1508 } 1509 1510 Bundle data = new Bundle(); 1511 Message msg = handler.obtainMessage(MSG_NATIVE_REQ_GET_TOTAL_NUM_OF_ITEMS); 1512 msg.arg1 = scope; 1513 msg.obj = address; 1514 handler.sendMessage(msg); 1515 } 1516 1517 private void notifyVolumeChanged(int volume) { 1518 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 1519 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); 1520 } 1521 1522 private int convertToAudioStreamVolume(int volume) { 1523 // Rescale volume to match AudioSystem's volume 1524 return (int) Math.floor((double) volume * mAudioStreamMax / AVRCP_MAX_VOL); 1525 } 1526 1527 private int convertToAvrcpVolume(int volume) { 1528 return (int) Math.ceil((double) volume * AVRCP_MAX_VOL / mAudioStreamMax); 1529 } 1530 1531 private void blackListCurrentDevice(String reason) { 1532 mFeatures &= ~BTRC_FEAT_ABSOLUTE_VOLUME; 1533 mAudioManager.avrcpSupportsAbsoluteVolume(mAddress, isAbsoluteVolumeSupported()); 1534 1535 SharedPreferences pref = 1536 mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST, Context.MODE_PRIVATE); 1537 SharedPreferences.Editor editor = pref.edit(); 1538 1539 StringBuilder sb = new StringBuilder(); 1540 sb.append("Time: "); 1541 sb.append(android.text.format.DateFormat.format("yyyy/MM/dd HH:mm:ss", 1542 System.currentTimeMillis())); 1543 sb.append(" Reason: "); 1544 sb.append(reason); 1545 editor.putString(mAddress, sb.toString()); 1546 editor.apply(); 1547 } 1548 1549 private int modifyRcFeatureFromBlacklist(int feature, String address) { 1550 SharedPreferences pref = 1551 mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST, Context.MODE_PRIVATE); 1552 if (!pref.contains(address)) { 1553 return feature; 1554 } 1555 return feature & ~BTRC_FEAT_ABSOLUTE_VOLUME; 1556 } 1557 1558 public void resetBlackList(String address) { 1559 SharedPreferences pref = 1560 mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST, Context.MODE_PRIVATE); 1561 SharedPreferences.Editor editor = pref.edit(); 1562 editor.remove(address); 1563 editor.apply(); 1564 } 1565 1566 /** 1567 * This is called from A2dpStateMachine to set A2dp audio state. 1568 */ 1569 public void setA2dpAudioState(int state) { 1570 final AvrcpMessageHandler handler = mHandler; 1571 if (handler == null) { 1572 if (DEBUG) Log.d(TAG, "setA2dpAudioState: mHandler is already null"); 1573 return; 1574 } 1575 1576 Message msg = handler.obtainMessage(MSG_SET_A2DP_AUDIO_STATE, state, 0); 1577 handler.sendMessage(msg); 1578 } 1579 1580 private class AvrcpServiceBootReceiver extends BroadcastReceiver { 1581 @Override 1582 public void onReceive(Context context, Intent intent) { 1583 String action = intent.getAction(); 1584 if (action.equals(Intent.ACTION_USER_UNLOCKED)) { 1585 if (DEBUG) { 1586 Log.d(TAG, "User unlocked, initializing player lists"); 1587 } 1588 /* initializing media player's list */ 1589 buildBrowsablePlayerList(); 1590 } 1591 } 1592 } 1593 1594 private class AvrcpServiceBroadcastReceiver extends BroadcastReceiver { 1595 @Override 1596 public void onReceive(Context context, Intent intent) { 1597 String action = intent.getAction(); 1598 if (DEBUG) { 1599 Log.d(TAG, "AvrcpServiceBroadcastReceiver-> Action: " + action); 1600 } 1601 1602 if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals( 1603 Intent.ACTION_PACKAGE_DATA_CLEARED)) { 1604 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 1605 // a package is being removed, not replaced 1606 String packageName = intent.getData().getSchemeSpecificPart(); 1607 if (packageName != null) { 1608 handlePackageModified(packageName, true); 1609 } 1610 } 1611 1612 } else if (action.equals(Intent.ACTION_PACKAGE_ADDED) || action.equals( 1613 Intent.ACTION_PACKAGE_CHANGED)) { 1614 String packageName = intent.getData().getSchemeSpecificPart(); 1615 if (DEBUG) { 1616 Log.d(TAG, "AvrcpServiceBroadcastReceiver-> packageName: " + packageName); 1617 } 1618 if (packageName != null) { 1619 handlePackageModified(packageName, false); 1620 } 1621 } 1622 } 1623 } 1624 1625 private void handlePackageModified(String packageName, boolean removed) { 1626 if (DEBUG) { 1627 Log.d(TAG, "packageName: " + packageName + " removed: " + removed); 1628 } 1629 1630 if (removed) { 1631 removeMediaPlayerInfo(packageName); 1632 // old package is removed, updating local browsable player's list 1633 if (isBrowseSupported(packageName)) { 1634 removePackageFromBrowseList(packageName); 1635 } 1636 } else { 1637 // new package has been added. 1638 if (isBrowsableListUpdated(packageName)) { 1639 // Rebuilding browsable players list 1640 buildBrowsablePlayerList(); 1641 } 1642 } 1643 } 1644 1645 private boolean isBrowsableListUpdated(String newPackageName) { 1646 // getting the browsable media players list from package manager 1647 Intent intent = new Intent("android.media.browse.MediaBrowserService"); 1648 List<ResolveInfo> resInfos = 1649 mPackageManager.queryIntentServices(intent, PackageManager.MATCH_ALL); 1650 for (ResolveInfo resolveInfo : resInfos) { 1651 if (resolveInfo.serviceInfo.packageName.equals(newPackageName)) { 1652 if (DEBUG) { 1653 Log.d(TAG, 1654 "isBrowsableListUpdated: package includes MediaBrowserService, true"); 1655 } 1656 return true; 1657 } 1658 } 1659 1660 // if list has different size 1661 if (resInfos.size() != mBrowsePlayerInfoList.size()) { 1662 if (DEBUG) { 1663 Log.d(TAG, "isBrowsableListUpdated: browsable list size mismatch, true"); 1664 } 1665 return true; 1666 } 1667 1668 Log.d(TAG, "isBrowsableListUpdated: false"); 1669 return false; 1670 } 1671 1672 private void removePackageFromBrowseList(String packageName) { 1673 if (DEBUG) { 1674 Log.d(TAG, "removePackageFromBrowseList: " + packageName); 1675 } 1676 synchronized (mBrowsePlayerInfoList) { 1677 int browseInfoID = getBrowseId(packageName); 1678 if (browseInfoID != -1) { 1679 mBrowsePlayerInfoList.remove(browseInfoID); 1680 } 1681 } 1682 } 1683 1684 /* 1685 * utility function to get the browse player index from global browsable 1686 * list. It may return -1 if specified package name is not in the list. 1687 */ 1688 private int getBrowseId(String packageName) { 1689 boolean response = false; 1690 int browseInfoID = 0; 1691 synchronized (mBrowsePlayerInfoList) { 1692 for (BrowsePlayerInfo info : mBrowsePlayerInfoList) { 1693 if (info.packageName.equals(packageName)) { 1694 response = true; 1695 break; 1696 } 1697 browseInfoID++; 1698 } 1699 } 1700 1701 if (!response) { 1702 browseInfoID = -1; 1703 } 1704 1705 if (DEBUG) { 1706 Log.d(TAG, "getBrowseId for packageName: " + packageName + " , browseInfoID: " 1707 + browseInfoID); 1708 } 1709 return browseInfoID; 1710 } 1711 1712 private void setAddressedPlayer(byte[] bdaddr, int selectedId) { 1713 String functionTag = "setAddressedPlayer(" + selectedId + "): "; 1714 1715 synchronized (mMediaPlayerInfoList) { 1716 if (mMediaPlayerInfoList.isEmpty()) { 1717 Log.w(TAG, functionTag + "no players, send no available players"); 1718 setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY); 1719 return; 1720 } 1721 if (!mMediaPlayerInfoList.containsKey(selectedId)) { 1722 Log.w(TAG, functionTag + "invalid id, sending response back "); 1723 setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_INV_PLAYER); 1724 return; 1725 } 1726 1727 if (isPlayerAlreadyAddressed(selectedId)) { 1728 MediaPlayerInfo info = getAddressedPlayerInfo(); 1729 Log.i(TAG, functionTag + "player already addressed: " + info); 1730 setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR); 1731 return; 1732 } 1733 // register new Media Controller Callback and update the current IDs 1734 if (!updateCurrentController(selectedId, mCurrBrowsePlayerID)) { 1735 Log.e(TAG, functionTag + "updateCurrentController failed!"); 1736 setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); 1737 return; 1738 } 1739 // If we don't have a controller, try to launch the player 1740 MediaPlayerInfo info = getAddressedPlayerInfo(); 1741 if (info.getMediaController() == null) { 1742 Intent launch = mPackageManager.getLaunchIntentForPackage(info.getPackageName()); 1743 Log.i(TAG, functionTag + "launching player " + launch); 1744 mContext.startActivity(launch); 1745 } 1746 } 1747 setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR); 1748 } 1749 1750 private void setBrowsedPlayer(byte[] bdaddr, int selectedId) { 1751 int status = AvrcpConstants.RSP_NO_ERROR; 1752 1753 // checking for error cases 1754 if (mMediaPlayerInfoList.isEmpty()) { 1755 status = AvrcpConstants.RSP_NO_AVBL_PLAY; 1756 Log.w(TAG, "setBrowsedPlayer: No available players! "); 1757 } else { 1758 // Workaround for broken controllers selecting ID 0 1759 // Seen at least on Ford, Chevrolet MyLink 1760 if (selectedId == 0) { 1761 Log.w(TAG, "setBrowsedPlayer: workaround invalid id 0"); 1762 selectedId = mCurrAddrPlayerID; 1763 } 1764 1765 // update current browse player id and start browsing service 1766 updateNewIds(mCurrAddrPlayerID, selectedId); 1767 String browsedPackage = getPackageName(selectedId); 1768 1769 if (!isPackageNameValid(browsedPackage)) { 1770 Log.w(TAG, " Invalid package for id:" + mCurrBrowsePlayerID); 1771 status = AvrcpConstants.RSP_INV_PLAYER; 1772 } else if (!isBrowseSupported(browsedPackage)) { 1773 Log.w(TAG, "Browse unsupported for id:" + mCurrBrowsePlayerID + ", packagename : " 1774 + browsedPackage); 1775 status = AvrcpConstants.RSP_PLAY_NOT_BROW; 1776 } else if (!startBrowseService(bdaddr, browsedPackage)) { 1777 Log.e(TAG, "service cannot be started for browse player id:" + mCurrBrowsePlayerID 1778 + ", packagename : " + browsedPackage); 1779 status = AvrcpConstants.RSP_INTERNAL_ERR; 1780 } 1781 } 1782 1783 if (status != AvrcpConstants.RSP_NO_ERROR) { 1784 setBrowsedPlayerRspNative(bdaddr, status, (byte) 0x00, 0, null); 1785 } 1786 1787 if (DEBUG) { 1788 Log.d(TAG, "setBrowsedPlayer for selectedId: " + selectedId + " , status: " + status); 1789 } 1790 } 1791 1792 private MediaSessionManager.OnActiveSessionsChangedListener mActiveSessionListener = 1793 new MediaSessionManager.OnActiveSessionsChangedListener() { 1794 1795 @Override 1796 public void onActiveSessionsChanged( 1797 List<android.media.session.MediaController> newControllers) { 1798 Set<String> updatedPackages = new HashSet<String>(); 1799 // Update the current players 1800 for (android.media.session.MediaController controller : newControllers) { 1801 String packageName = controller.getPackageName(); 1802 if (DEBUG) { 1803 Log.v(TAG, "ActiveSession: " + MediaControllerFactory.wrap(controller)); 1804 } 1805 // Only use the first (highest priority) controller from each package 1806 if (updatedPackages.contains(packageName)) { 1807 continue; 1808 } 1809 addMediaPlayerController(controller); 1810 updatedPackages.add(packageName); 1811 } 1812 1813 if (newControllers.size() > 0 && getAddressedPlayerInfo() == null) { 1814 if (DEBUG) { 1815 Log.v(TAG, "No addressed player but active sessions, taking first."); 1816 } 1817 setAddressedMediaSessionPackage(newControllers.get(0).getPackageName()); 1818 } 1819 updateCurrentMediaState(); 1820 } 1821 }; 1822 1823 private void setAddressedMediaSessionPackage(@Nullable String packageName) { 1824 if (packageName == null) { 1825 // Should only happen when there's no media players, reset to no available player. 1826 updateCurrentController(0, mCurrBrowsePlayerID); 1827 return; 1828 } 1829 if (packageName.equals("com.android.server.telecom")) { 1830 Log.d(TAG, "Ignore addressed media session change to telecom"); 1831 return; 1832 } 1833 // No change. 1834 if (getPackageName(mCurrAddrPlayerID).equals(packageName)) { 1835 return; 1836 } 1837 if (DEBUG) { 1838 Log.v(TAG, "Changing addressed media session to " + packageName); 1839 } 1840 // If the player doesn't exist, we need to add it. 1841 if (getMediaPlayerInfo(packageName) == null) { 1842 addMediaPlayerPackage(packageName); 1843 updateCurrentMediaState(); 1844 } 1845 synchronized (mMediaPlayerInfoList) { 1846 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) { 1847 if (entry.getValue().getPackageName().equals(packageName)) { 1848 int newAddrID = entry.getKey(); 1849 if (DEBUG) { 1850 Log.v(TAG, "Set addressed #" + newAddrID + " " + entry.getValue()); 1851 } 1852 updateCurrentController(newAddrID, mCurrBrowsePlayerID); 1853 updateCurrentMediaState(); 1854 return; 1855 } 1856 } 1857 } 1858 // We shouldn't ever get here. 1859 Log.e(TAG, "Player info for " + packageName + " doesn't exist!"); 1860 } 1861 1862 private void setActiveMediaSession(MediaSession.Token token) { 1863 android.media.session.MediaController activeController = 1864 new android.media.session.MediaController(mContext, token); 1865 if (activeController.getPackageName().equals("com.android.server.telecom")) { 1866 Log.d(TAG, "Ignore active media session change to telecom"); 1867 return; 1868 } 1869 if (DEBUG) { 1870 Log.v(TAG, "Set active media session " + activeController.getPackageName()); 1871 } 1872 addMediaPlayerController(activeController); 1873 setAddressedMediaSessionPackage(activeController.getPackageName()); 1874 } 1875 1876 private boolean startBrowseService(byte[] bdaddr, String packageName) { 1877 boolean status = true; 1878 1879 /* creating new instance for Browse Media Player */ 1880 String browseService = getBrowseServiceName(packageName); 1881 if (!browseService.isEmpty()) { 1882 mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) 1883 .setBrowsed(packageName, browseService); 1884 } else { 1885 Log.w(TAG, "No Browser service available for " + packageName); 1886 status = false; 1887 } 1888 1889 if (DEBUG) { 1890 Log.d(TAG, 1891 "startBrowseService for packageName: " + packageName + ", status = " + status); 1892 } 1893 return status; 1894 } 1895 1896 private String getBrowseServiceName(String packageName) { 1897 String browseServiceName = ""; 1898 1899 // getting the browse service name from browse player info 1900 synchronized (mBrowsePlayerInfoList) { 1901 int browseInfoID = getBrowseId(packageName); 1902 if (browseInfoID != -1) { 1903 browseServiceName = mBrowsePlayerInfoList.get(browseInfoID).serviceClass; 1904 } 1905 } 1906 1907 if (DEBUG) { 1908 Log.d(TAG, "getBrowseServiceName for packageName: " + packageName 1909 + ", browseServiceName = " + browseServiceName); 1910 } 1911 return browseServiceName; 1912 } 1913 1914 void buildBrowsablePlayerList() { 1915 synchronized (mBrowsePlayerInfoList) { 1916 mBrowsePlayerInfoList.clear(); 1917 Intent intent = new Intent(android.service.media.MediaBrowserService.SERVICE_INTERFACE); 1918 List<ResolveInfo> playerList = 1919 mPackageManager.queryIntentServices(intent, PackageManager.MATCH_ALL); 1920 1921 for (ResolveInfo info : playerList) { 1922 String displayableName = info.loadLabel(mPackageManager).toString(); 1923 String serviceName = info.serviceInfo.name; 1924 String packageName = info.serviceInfo.packageName; 1925 1926 if (DEBUG) { 1927 Log.d(TAG, "Adding " + serviceName + " to list of browsable players"); 1928 } 1929 BrowsePlayerInfo currentPlayer = 1930 new BrowsePlayerInfo(packageName, displayableName, serviceName); 1931 mBrowsePlayerInfoList.add(currentPlayer); 1932 MediaPlayerInfo playerInfo = getMediaPlayerInfo(packageName); 1933 MediaController controller = 1934 (playerInfo == null) ? null : playerInfo.getMediaController(); 1935 // Refresh the media player entry so it notices we can browse 1936 if (controller != null) { 1937 addMediaPlayerController(controller.getWrappedInstance()); 1938 } else { 1939 addMediaPlayerPackage(packageName); 1940 } 1941 } 1942 updateCurrentMediaState(); 1943 } 1944 } 1945 1946 /* Initializes list of media players identified from session manager active sessions */ 1947 private void initMediaPlayersList() { 1948 synchronized (mMediaPlayerInfoList) { 1949 // Clearing old browsable player's list 1950 mMediaPlayerInfoList.clear(); 1951 1952 if (mMediaSessionManager == null) { 1953 if (DEBUG) { 1954 Log.w(TAG, "initMediaPlayersList: no media session manager!"); 1955 } 1956 return; 1957 } 1958 1959 List<android.media.session.MediaController> controllers = 1960 mMediaSessionManager.getActiveSessions(null); 1961 if (DEBUG) { 1962 Log.v(TAG, "initMediaPlayerInfoList: " + controllers.size() + " controllers"); 1963 } 1964 /* Initializing all media players */ 1965 for (android.media.session.MediaController controller : controllers) { 1966 addMediaPlayerController(controller); 1967 } 1968 1969 updateCurrentMediaState(); 1970 1971 if (mMediaPlayerInfoList.size() > 0) { 1972 // Set the first one as the Addressed Player 1973 updateCurrentController(mMediaPlayerInfoList.firstKey(), -1); 1974 } 1975 } 1976 } 1977 1978 private List<android.media.session.MediaController> getMediaControllers() { 1979 List<android.media.session.MediaController> controllers = 1980 new ArrayList<android.media.session.MediaController>(); 1981 synchronized (mMediaPlayerInfoList) { 1982 for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) { 1983 MediaController controller = info.getMediaController(); 1984 if (controller != null) { 1985 controllers.add(controller.getWrappedInstance()); 1986 } 1987 } 1988 } 1989 return controllers; 1990 } 1991 1992 /** Add (or update) a player to the media player list without a controller */ 1993 private boolean addMediaPlayerPackage(String packageName) { 1994 MediaPlayerInfo info = new MediaPlayerInfo(null, AvrcpConstants.PLAYER_TYPE_AUDIO, 1995 AvrcpConstants.PLAYER_SUBTYPE_NONE, PLAYSTATUS_STOPPED, 1996 getFeatureBitMask(packageName), packageName, getAppLabel(packageName)); 1997 return addMediaPlayerInfo(info); 1998 } 1999 2000 /** Add (or update) a player to the media player list given an active controller */ 2001 private boolean addMediaPlayerController(android.media.session.MediaController controller) { 2002 String packageName = controller.getPackageName(); 2003 MediaPlayerInfo info = new MediaPlayerInfo(MediaControllerFactory.wrap(controller), 2004 AvrcpConstants.PLAYER_TYPE_AUDIO, AvrcpConstants.PLAYER_SUBTYPE_NONE, 2005 getBluetoothPlayState(controller.getPlaybackState()), 2006 getFeatureBitMask(packageName), controller.getPackageName(), 2007 getAppLabel(packageName)); 2008 return addMediaPlayerInfo(info); 2009 } 2010 2011 /** Add or update a player to the media player list given the MediaPlayerInfo object. 2012 * @return true if an item was updated, false if it was added instead 2013 */ 2014 private boolean addMediaPlayerInfo(MediaPlayerInfo info) { 2015 int updateId = -1; 2016 boolean updated = false; 2017 boolean currentRemoved = false; 2018 if (info.getPackageName().equals("com.android.server.telecom")) { 2019 Log.d(TAG, "Skip adding telecom to the media player info list"); 2020 return updated; 2021 } 2022 synchronized (mMediaPlayerInfoList) { 2023 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) { 2024 MediaPlayerInfo current = entry.getValue(); 2025 int id = entry.getKey(); 2026 if (info.getPackageName().equals(current.getPackageName())) { 2027 if (!current.equalView(info)) { 2028 // If we would present a different player, make it a new player 2029 // so that controllers know whether a player is browsable or not. 2030 mMediaPlayerInfoList.remove(id); 2031 currentRemoved = (mCurrAddrPlayerID == id); 2032 break; 2033 } 2034 updateId = id; 2035 updated = true; 2036 break; 2037 } 2038 } 2039 if (updateId == -1) { 2040 // New player 2041 mLastUsedPlayerID++; 2042 updateId = mLastUsedPlayerID; 2043 mAvailablePlayerViewChanged = true; 2044 } 2045 mMediaPlayerInfoList.put(updateId, info); 2046 } 2047 if (DEBUG) { 2048 Log.d(TAG, (updated ? "update #" : "add #") + updateId + ":" + info.toString()); 2049 } 2050 if (currentRemoved || updateId == mCurrAddrPlayerID) { 2051 updateCurrentController(updateId, mCurrBrowsePlayerID); 2052 } 2053 return updated; 2054 } 2055 2056 /** Remove all players related to |packageName| from the media player info list */ 2057 private MediaPlayerInfo removeMediaPlayerInfo(String packageName) { 2058 synchronized (mMediaPlayerInfoList) { 2059 int removeKey = -1; 2060 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) { 2061 if (entry.getValue().getPackageName().equals(packageName)) { 2062 removeKey = entry.getKey(); 2063 break; 2064 } 2065 } 2066 if (removeKey != -1) { 2067 if (DEBUG) { 2068 Log.d(TAG, "remove #" + removeKey + ":" + mMediaPlayerInfoList.get(removeKey)); 2069 } 2070 mAvailablePlayerViewChanged = true; 2071 return mMediaPlayerInfoList.remove(removeKey); 2072 } 2073 2074 return null; 2075 } 2076 } 2077 2078 /** Remove the controller referenced by |controller| from any player in the list */ 2079 private void removeMediaController(@Nullable android.media.session.MediaController controller) { 2080 if (controller == null) { 2081 return; 2082 } 2083 synchronized (mMediaPlayerInfoList) { 2084 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) { 2085 MediaPlayerInfo info = entry.getValue(); 2086 MediaController c = info.getMediaController(); 2087 if (c != null && c.equals(controller)) { 2088 info.setMediaController(null); 2089 if (entry.getKey() == mCurrAddrPlayerID) { 2090 updateCurrentController(mCurrAddrPlayerID, mCurrBrowsePlayerID); 2091 } 2092 } 2093 } 2094 } 2095 } 2096 2097 /* 2098 * utility function to get the playback state of any media player through 2099 * media controller APIs. 2100 */ 2101 private byte getBluetoothPlayState(PlaybackState pbState) { 2102 if (pbState == null) { 2103 Log.w(TAG, "playState object null, sending STOPPED"); 2104 return PLAYSTATUS_STOPPED; 2105 } 2106 2107 switch (pbState.getState()) { 2108 case PlaybackState.STATE_PLAYING: 2109 return PLAYSTATUS_PLAYING; 2110 2111 case PlaybackState.STATE_BUFFERING: 2112 case PlaybackState.STATE_STOPPED: 2113 case PlaybackState.STATE_NONE: 2114 case PlaybackState.STATE_CONNECTING: 2115 return PLAYSTATUS_STOPPED; 2116 2117 case PlaybackState.STATE_PAUSED: 2118 return PLAYSTATUS_PAUSED; 2119 2120 case PlaybackState.STATE_FAST_FORWARDING: 2121 case PlaybackState.STATE_SKIPPING_TO_NEXT: 2122 case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM: 2123 return PLAYSTATUS_FWD_SEEK; 2124 2125 case PlaybackState.STATE_REWINDING: 2126 case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: 2127 return PLAYSTATUS_REV_SEEK; 2128 2129 case PlaybackState.STATE_ERROR: 2130 default: 2131 return PLAYSTATUS_ERROR; 2132 } 2133 } 2134 2135 /* 2136 * utility function to get the feature bit mask of any media player through 2137 * package name 2138 */ 2139 private short[] getFeatureBitMask(String packageName) { 2140 2141 ArrayList<Short> featureBitsList = new ArrayList<Short>(); 2142 2143 /* adding default feature bits */ 2144 featureBitsList.add(AvrcpConstants.AVRC_PF_PLAY_BIT_NO); 2145 featureBitsList.add(AvrcpConstants.AVRC_PF_STOP_BIT_NO); 2146 featureBitsList.add(AvrcpConstants.AVRC_PF_PAUSE_BIT_NO); 2147 featureBitsList.add(AvrcpConstants.AVRC_PF_REWIND_BIT_NO); 2148 featureBitsList.add(AvrcpConstants.AVRC_PF_FAST_FWD_BIT_NO); 2149 featureBitsList.add(AvrcpConstants.AVRC_PF_FORWARD_BIT_NO); 2150 featureBitsList.add(AvrcpConstants.AVRC_PF_BACKWARD_BIT_NO); 2151 featureBitsList.add(AvrcpConstants.AVRC_PF_ADV_CTRL_BIT_NO); 2152 2153 /* Add/Modify browse player supported features. */ 2154 if (isBrowseSupported(packageName)) { 2155 featureBitsList.add(AvrcpConstants.AVRC_PF_BROWSE_BIT_NO); 2156 featureBitsList.add(AvrcpConstants.AVRC_PF_UID_UNIQUE_BIT_NO); 2157 featureBitsList.add(AvrcpConstants.AVRC_PF_NOW_PLAY_BIT_NO); 2158 featureBitsList.add(AvrcpConstants.AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO); 2159 } 2160 2161 // converting arraylist to array for response 2162 short[] featureBitsArray = new short[featureBitsList.size()]; 2163 2164 for (int i = 0; i < featureBitsList.size(); i++) { 2165 featureBitsArray[i] = featureBitsList.get(i).shortValue(); 2166 } 2167 2168 return featureBitsArray; 2169 } 2170 2171 /** 2172 * Checks the Package name if it supports Browsing or not. 2173 * 2174 * @param packageName - name of the package to get the Id. 2175 * @return true if it supports browsing, else false. 2176 */ 2177 private boolean isBrowseSupported(String packageName) { 2178 synchronized (mBrowsePlayerInfoList) { 2179 /* check if Browsable Player's list contains this package name */ 2180 for (BrowsePlayerInfo info : mBrowsePlayerInfoList) { 2181 if (info.packageName.equals(packageName)) { 2182 if (DEBUG) { 2183 Log.v(TAG, "isBrowseSupported for " + packageName + ": true"); 2184 } 2185 return true; 2186 } 2187 } 2188 } 2189 2190 if (DEBUG) { 2191 Log.v(TAG, "isBrowseSupported for " + packageName + ": false"); 2192 } 2193 return false; 2194 } 2195 2196 private String getPackageName(int id) { 2197 MediaPlayerInfo player = null; 2198 synchronized (mMediaPlayerInfoList) { 2199 player = mMediaPlayerInfoList.getOrDefault(id, null); 2200 } 2201 2202 if (player == null) { 2203 Log.w(TAG, "No package name for player (" + id + " not valid)"); 2204 return ""; 2205 } 2206 2207 String packageName = player.getPackageName(); 2208 if (DEBUG) { 2209 Log.v(TAG, "Player " + id + " package: " + packageName); 2210 } 2211 return packageName; 2212 } 2213 2214 /* from the global object, getting the current browsed player's package name */ 2215 private String getCurrentBrowsedPlayer(byte[] bdaddr) { 2216 String browsedPlayerPackage = ""; 2217 2218 Map<String, BrowsedMediaPlayer> connList = mAvrcpBrowseManager.getConnList(); 2219 String bdaddrStr = new String(bdaddr); 2220 if (connList.containsKey(bdaddrStr)) { 2221 browsedPlayerPackage = connList.get(bdaddrStr).getPackageName(); 2222 } 2223 if (DEBUG) { 2224 Log.v(TAG, "getCurrentBrowsedPlayerPackage: " + browsedPlayerPackage); 2225 } 2226 return browsedPlayerPackage; 2227 } 2228 2229 /* Returns the MediaPlayerInfo for the currently addressed media player */ 2230 private MediaPlayerInfo getAddressedPlayerInfo() { 2231 synchronized (mMediaPlayerInfoList) { 2232 return mMediaPlayerInfoList.getOrDefault(mCurrAddrPlayerID, null); 2233 } 2234 } 2235 2236 /* 2237 * Utility function to get the Media player info from package name returns 2238 * null if package name not found in media players list 2239 */ 2240 private MediaPlayerInfo getMediaPlayerInfo(String packageName) { 2241 synchronized (mMediaPlayerInfoList) { 2242 if (mMediaPlayerInfoList.isEmpty()) { 2243 if (DEBUG) { 2244 Log.v(TAG, "getMediaPlayerInfo: Media players list empty"); 2245 } 2246 return null; 2247 } 2248 2249 for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) { 2250 if (packageName.equals(info.getPackageName())) { 2251 if (DEBUG) { 2252 Log.v(TAG, "getMediaPlayerInfo: Found " + packageName); 2253 } 2254 return info; 2255 } 2256 } 2257 if (DEBUG) { 2258 Log.w(TAG, "getMediaPlayerInfo: " + packageName + " not found"); 2259 } 2260 return null; 2261 } 2262 } 2263 2264 /* prepare media list & return the media player list response object */ 2265 private MediaPlayerListRsp prepareMediaPlayerRspObj() { 2266 synchronized (mMediaPlayerInfoList) { 2267 // TODO(apanicke): This hack will go away as soon as a developer 2268 // option to enable or disable player selection is created. Right 2269 // now this is needed to fix BMW i3 carkits and any other carkits 2270 // that might try to connect to a player that isnt the current 2271 // player based on this list 2272 int numPlayers = 1; 2273 2274 int[] playerIds = new int[numPlayers]; 2275 byte[] playerTypes = new byte[numPlayers]; 2276 int[] playerSubTypes = new int[numPlayers]; 2277 String[] displayableNameArray = new String[numPlayers]; 2278 byte[] playStatusValues = new byte[numPlayers]; 2279 short[] featureBitMaskValues = 2280 new short[numPlayers * AvrcpConstants.AVRC_FEATURE_MASK_SIZE]; 2281 2282 // Reserve the first spot for the currently addressed player if 2283 // we have one 2284 int players = mMediaPlayerInfoList.containsKey(mCurrAddrPlayerID) ? 1 : 0; 2285 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) { 2286 int idx = players; 2287 if (entry.getKey() == mCurrAddrPlayerID) { 2288 idx = 0; 2289 } else { 2290 continue; // TODO(apanicke): Remove, see above note 2291 } 2292 MediaPlayerInfo info = entry.getValue(); 2293 playerIds[idx] = entry.getKey(); 2294 playerTypes[idx] = info.getMajorType(); 2295 playerSubTypes[idx] = info.getSubType(); 2296 displayableNameArray[idx] = info.getDisplayableName(); 2297 playStatusValues[idx] = info.getPlayStatus(); 2298 2299 short[] featureBits = info.getFeatureBitMask(); 2300 for (int numBit = 0; numBit < featureBits.length; numBit++) { 2301 /* gives which octet this belongs to */ 2302 byte octet = (byte) (featureBits[numBit] / 8); 2303 /* gives the bit position within the octet */ 2304 byte bit = (byte) (featureBits[numBit] % 8); 2305 featureBitMaskValues[(idx * AvrcpConstants.AVRC_FEATURE_MASK_SIZE) + octet] |= 2306 (1 << bit); 2307 } 2308 2309 /* printLogs */ 2310 if (DEBUG) { 2311 Log.d(TAG, "Player " + playerIds[idx] + ": " + displayableNameArray[idx] 2312 + " type: " + playerTypes[idx] + ", " + playerSubTypes[idx] 2313 + " status: " + playStatusValues[idx]); 2314 } 2315 2316 if (idx != 0) { 2317 players++; 2318 } 2319 } 2320 2321 if (DEBUG) { 2322 Log.d(TAG, "prepareMediaPlayerRspObj: numPlayers = " + numPlayers); 2323 } 2324 2325 return new MediaPlayerListRsp(AvrcpConstants.RSP_NO_ERROR, sUIDCounter, numPlayers, 2326 AvrcpConstants.BTRC_ITEM_PLAYER, playerIds, playerTypes, playerSubTypes, 2327 playStatusValues, featureBitMaskValues, displayableNameArray); 2328 } 2329 } 2330 2331 /* build media player list and send it to remote. */ 2332 private void handleMediaPlayerListRsp(AvrcpCmd.FolderItemsCmd folderObj) { 2333 MediaPlayerListRsp rspObj = null; 2334 synchronized (mMediaPlayerInfoList) { 2335 int numPlayers = mMediaPlayerInfoList.size(); 2336 if (numPlayers == 0) { 2337 mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY, 2338 (short) 0, (byte) 0, 0, null, null, null, null, null, null); 2339 return; 2340 } 2341 if (folderObj.mStartItem >= numPlayers) { 2342 Log.i(TAG, "handleMediaPlayerListRsp: start = " + folderObj.mStartItem 2343 + " > num of items = " + numPlayers); 2344 mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants.RSP_INV_RANGE, 2345 (short) 0, (byte) 0, 0, null, null, null, null, null, null); 2346 return; 2347 } 2348 rspObj = prepareMediaPlayerRspObj(); 2349 } 2350 if (DEBUG) { 2351 Log.d(TAG, "handleMediaPlayerListRsp: sending " + rspObj.mNumItems + " players"); 2352 } 2353 mediaPlayerListRspNative(folderObj.mAddress, rspObj.mStatus, rspObj.mUIDCounter, 2354 rspObj.mItemType, rspObj.mNumItems, rspObj.mPlayerIds, rspObj.mPlayerTypes, 2355 rspObj.mPlayerSubTypes, rspObj.mPlayStatusValues, rspObj.mFeatureBitMaskValues, 2356 rspObj.mPlayerNameList); 2357 } 2358 2359 /* unregister to the old controller, update new IDs and register to the new controller */ 2360 private boolean updateCurrentController(int addrId, int browseId) { 2361 boolean registerRsp = true; 2362 2363 updateNewIds(addrId, browseId); 2364 2365 MediaController newController = null; 2366 MediaPlayerInfo info = getAddressedPlayerInfo(); 2367 if (info != null) { 2368 newController = info.getMediaController(); 2369 } 2370 2371 if (DEBUG) { 2372 Log.d(TAG, "updateCurrentController: " + mMediaController + " to " + newController); 2373 } 2374 synchronized (this) { 2375 if (mMediaController == null || (!mMediaController.equals(newController))) { 2376 if (mMediaController != null) { 2377 mMediaController.unregisterCallback(mMediaControllerCb); 2378 } 2379 mMediaController = newController; 2380 if (mMediaController != null) { 2381 mMediaController.registerCallback(mMediaControllerCb, mHandler); 2382 } else { 2383 registerRsp = false; 2384 } 2385 } 2386 } 2387 updateCurrentMediaState(); 2388 return registerRsp; 2389 } 2390 2391 /* Handle getfolderitems for scope = VFS, Search, NowPlayingList */ 2392 private void handleGetFolderItemBrowseResponse(AvrcpCmd.FolderItemsCmd folderObj, 2393 byte[] bdaddr) { 2394 int status = AvrcpConstants.RSP_NO_ERROR; 2395 2396 /* Browsed player is already set */ 2397 if (folderObj.mScope == AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM) { 2398 if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) == null) { 2399 Log.e(TAG, "handleGetFolderItemBrowseResponse: no browsed player set for " 2400 + Utils.getAddressStringFromByte(bdaddr)); 2401 getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, (short) 0, 2402 (byte) 0x00, 0, null, null, null, null, null, null, null, null); 2403 return; 2404 } 2405 mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getFolderItemsVFS(folderObj); 2406 return; 2407 } 2408 if (folderObj.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) { 2409 mAddressedMediaPlayer.getFolderItemsNowPlaying(bdaddr, folderObj, mMediaController); 2410 return; 2411 } 2412 2413 /* invalid scope */ 2414 Log.e(TAG, "handleGetFolderItemBrowseResponse: unknown scope " + folderObj.mScope); 2415 getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INV_SCOPE, (short) 0, (byte) 0x00, 0, 2416 null, null, null, null, null, null, null, null); 2417 } 2418 2419 /* utility function to update the global values of current Addressed and browsed player */ 2420 private void updateNewIds(int addrId, int browseId) { 2421 if (DEBUG) { 2422 Log.v(TAG, 2423 "updateNewIds: Addressed:" + mCurrAddrPlayerID + " to " + addrId + ", Browse:" 2424 + mCurrBrowsePlayerID + " to " + browseId); 2425 } 2426 mCurrAddrPlayerID = addrId; 2427 mCurrBrowsePlayerID = browseId; 2428 } 2429 2430 /* Getting the application's displayable name from package name */ 2431 private String getAppLabel(String packageName) { 2432 ApplicationInfo appInfo = null; 2433 try { 2434 appInfo = mPackageManager.getApplicationInfo(packageName, 0); 2435 } catch (NameNotFoundException e) { 2436 e.printStackTrace(); 2437 } 2438 2439 return (String) (appInfo != null ? mPackageManager.getApplicationLabel(appInfo) 2440 : "Unknown"); 2441 } 2442 2443 private void handlePlayItemResponse(byte[] bdaddr, byte[] uid, byte scope) { 2444 if (scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) { 2445 mAddressedMediaPlayer.playItem(bdaddr, uid, mMediaController); 2446 } else { 2447 if (!isAddrPlayerSameAsBrowsed(bdaddr)) { 2448 Log.w(TAG, "Remote requesting play item on uid which may not be recognized by" 2449 + "current addressed player"); 2450 playItemRspNative(bdaddr, AvrcpConstants.RSP_INV_ITEM); 2451 } 2452 2453 if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) { 2454 mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).playItem(uid, scope); 2455 } else { 2456 Log.e(TAG, "handlePlayItemResponse: Remote requested playitem " 2457 + "before setbrowsedplayer"); 2458 playItemRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR); 2459 } 2460 } 2461 } 2462 2463 private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) { 2464 if (itemAttr.mUidCounter != sUIDCounter) { 2465 Log.e(TAG, "handleGetItemAttr: invaild uid counter."); 2466 getItemAttrRspNative(itemAttr.mAddress, AvrcpConstants.RSP_UID_CHANGED, (byte) 0, null, 2467 null); 2468 return; 2469 } 2470 if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) { 2471 if (mCurrAddrPlayerID == NO_PLAYER_ID) { 2472 getItemAttrRspNative(itemAttr.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY, (byte) 0, 2473 null, null); 2474 return; 2475 } 2476 mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController); 2477 return; 2478 } 2479 // All other scopes use browsed player 2480 if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null) { 2481 mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress).getItemAttr(itemAttr); 2482 } else { 2483 Log.e(TAG, "Could not get attributes. mBrowsedMediaPlayer is null"); 2484 getItemAttrRspNative(itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0, null, 2485 null); 2486 } 2487 } 2488 2489 private void handleGetTotalNumOfItemsResponse(byte[] bdaddr, byte scope) { 2490 // for scope as media player list 2491 if (scope == AvrcpConstants.BTRC_SCOPE_PLAYER_LIST) { 2492 int numPlayers = 0; 2493 synchronized (mMediaPlayerInfoList) { 2494 numPlayers = mMediaPlayerInfoList.size(); 2495 } 2496 if (DEBUG) { 2497 Log.d(TAG, "handleGetTotalNumOfItemsResponse: " + numPlayers + " players."); 2498 } 2499 getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, numPlayers); 2500 } else if (scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) { 2501 mAddressedMediaPlayer.getTotalNumOfItems(bdaddr, mMediaController); 2502 } else { 2503 // for FileSystem browsing scopes as VFS, Now Playing 2504 if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) { 2505 mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getTotalNumOfItems(scope); 2506 } else { 2507 Log.e(TAG, "Could not get Total NumOfItems. mBrowsedMediaPlayer is null"); 2508 getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, 0, 0); 2509 } 2510 } 2511 2512 } 2513 2514 /* check if browsed player and addressed player are same */ 2515 private boolean isAddrPlayerSameAsBrowsed(byte[] bdaddr) { 2516 String browsedPlayer = getCurrentBrowsedPlayer(bdaddr); 2517 2518 if (!isPackageNameValid(browsedPlayer)) { 2519 Log.w(TAG, "Browsed player name empty"); 2520 return false; 2521 } 2522 2523 MediaPlayerInfo info = getAddressedPlayerInfo(); 2524 String packageName = (info == null) ? "<none>" : info.getPackageName(); 2525 if (info == null || !packageName.equals(browsedPlayer)) { 2526 if (DEBUG) { 2527 Log.d(TAG, browsedPlayer + " is not addressed player " + packageName); 2528 } 2529 return false; 2530 } 2531 return true; 2532 } 2533 2534 /* checks if package name is not null or empty */ 2535 private boolean isPackageNameValid(String browsedPackage) { 2536 boolean isValid = (browsedPackage != null && browsedPackage.length() > 0); 2537 if (DEBUG) { 2538 Log.d(TAG, "isPackageNameValid: browsedPackage = " + browsedPackage + "isValid = " 2539 + isValid); 2540 } 2541 return isValid; 2542 } 2543 2544 /* checks if selected addressed player is already addressed */ 2545 private boolean isPlayerAlreadyAddressed(int selectedId) { 2546 // checking if selected ID is same as the current addressed player id 2547 boolean isAddressed = (mCurrAddrPlayerID == selectedId); 2548 if (DEBUG) { 2549 Log.d(TAG, "isPlayerAlreadyAddressed: isAddressed = " + isAddressed); 2550 } 2551 return isAddressed; 2552 } 2553 2554 public void dump(StringBuilder sb) { 2555 sb.append("AVRCP:\n"); 2556 ProfileService.println(sb, "mMediaAttributes: " + mMediaAttributes.toRedactedString()); 2557 ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags); 2558 ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState); 2559 ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT); 2560 ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT); 2561 ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs); 2562 ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT); 2563 ProfileService.println(sb, "mNextPosMs: " + mNextPosMs); 2564 ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs); 2565 ProfileService.println(sb, "mFeatures: " + mFeatures); 2566 ProfileService.println(sb, "mRemoteVolume: " + mRemoteVolume); 2567 ProfileService.println(sb, "mLastRemoteVolume: " + mLastRemoteVolume); 2568 ProfileService.println(sb, "mLastDirection: " + mLastDirection); 2569 ProfileService.println(sb, "mVolumeStep: " + mVolumeStep); 2570 ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax); 2571 ProfileService.println(sb, "mVolCmdSetInProgress: " + mVolCmdSetInProgress); 2572 ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes); 2573 ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString()); 2574 synchronized (this) { 2575 if (mMediaController != null) { 2576 ProfileService.println(sb, 2577 "mMediaController: " + mMediaController.getWrappedInstance() + " pkg " 2578 + mMediaController.getPackageName()); 2579 } 2580 } 2581 ProfileService.println(sb, ""); 2582 ProfileService.println(sb, "Media Players:"); 2583 synchronized (mMediaPlayerInfoList) { 2584 for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) { 2585 int key = entry.getKey(); 2586 ProfileService.println(sb, 2587 ((mCurrAddrPlayerID == key) ? " *#" : " #") + entry.getKey() + ": " + entry 2588 .getValue()); 2589 } 2590 } 2591 2592 ProfileService.println(sb, ""); 2593 mAddressedMediaPlayer.dump(sb, mMediaController); 2594 2595 ProfileService.println(sb, ""); 2596 ProfileService.println(sb, mPassthroughDispatched + " passthrough operations: "); 2597 if (mPassthroughDispatched > mPassthroughLogs.size()) { 2598 ProfileService.println(sb, " (last " + mPassthroughLogs.size() + ")"); 2599 } 2600 synchronized (mPassthroughLogs) { 2601 for (MediaKeyLog log : mPassthroughLogs) { 2602 ProfileService.println(sb, " " + log); 2603 } 2604 } 2605 synchronized (mPassthroughPending) { 2606 for (MediaKeyLog log : mPassthroughPending) { 2607 ProfileService.println(sb, " " + log); 2608 } 2609 } 2610 2611 // Print the blacklisted devices (for absolute volume control) 2612 SharedPreferences pref = 2613 mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST, Context.MODE_PRIVATE); 2614 Map<String, ?> allKeys = pref.getAll(); 2615 ProfileService.println(sb, ""); 2616 ProfileService.println(sb, "Runtime Blacklisted Devices (absolute volume):"); 2617 if (allKeys.isEmpty()) { 2618 ProfileService.println(sb, " None"); 2619 } else { 2620 for (Map.Entry<String, ?> entry : allKeys.entrySet()) { 2621 String key = entry.getKey(); 2622 Object value = entry.getValue(); 2623 if (value instanceof String) { 2624 ProfileService.println(sb, " " + key + " " + value); 2625 } else { 2626 ProfileService.println(sb, " " + key + " Reason: Unknown"); 2627 } 2628 } 2629 } 2630 } 2631 2632 public class AvrcpBrowseManager { 2633 public Map<String, BrowsedMediaPlayer> connList = new HashMap<String, BrowsedMediaPlayer>(); 2634 private AvrcpMediaRspInterface mMediaInterface; 2635 private Context mContext; 2636 2637 public AvrcpBrowseManager(Context context, AvrcpMediaRspInterface mediaInterface) { 2638 mContext = context; 2639 mMediaInterface = mediaInterface; 2640 } 2641 2642 public void cleanup() { 2643 Iterator entries = connList.entrySet().iterator(); 2644 while (entries.hasNext()) { 2645 Map.Entry entry = (Map.Entry) entries.next(); 2646 BrowsedMediaPlayer browsedMediaPlayer = (BrowsedMediaPlayer) entry.getValue(); 2647 if (browsedMediaPlayer != null) { 2648 browsedMediaPlayer.cleanup(); 2649 } 2650 } 2651 // clean up the map 2652 connList.clear(); 2653 } 2654 2655 // get the a free media player interface based on the passed bd address 2656 // if the no items is found for the passed media player then it assignes a 2657 // available media player interface 2658 public BrowsedMediaPlayer getBrowsedMediaPlayer(byte[] bdaddr) { 2659 BrowsedMediaPlayer mediaPlayer; 2660 String bdaddrStr = new String(bdaddr); 2661 if (connList.containsKey(bdaddrStr)) { 2662 mediaPlayer = connList.get(bdaddrStr); 2663 } else { 2664 mediaPlayer = new BrowsedMediaPlayer(bdaddr, mContext, mMediaInterface); 2665 connList.put(bdaddrStr, mediaPlayer); 2666 } 2667 return mediaPlayer; 2668 } 2669 2670 // clears the details pertaining to passed bdaddres 2671 public boolean clearBrowsedMediaPlayer(byte[] bdaddr) { 2672 String bdaddrStr = new String(bdaddr); 2673 if (connList.containsKey(bdaddrStr)) { 2674 connList.remove(bdaddrStr); 2675 return true; 2676 } 2677 return false; 2678 } 2679 2680 public Map<String, BrowsedMediaPlayer> getConnList() { 2681 return connList; 2682 } 2683 2684 /* Helper function to convert colon separated bdaddr to byte string */ 2685 private byte[] hexStringToByteArray(String s) { 2686 int len = s.length(); 2687 byte[] data = new byte[len / 2]; 2688 for (int i = 0; i < len; i += 2) { 2689 data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit( 2690 s.charAt(i + 1), 16)); 2691 } 2692 return data; 2693 } 2694 } 2695 2696 /* 2697 * private class which handles responses from AvrcpMediaManager. Maps responses to native 2698 * responses. This class implements the AvrcpMediaRspInterface interface. 2699 */ 2700 private class AvrcpMediaRsp implements AvrcpMediaRspInterface { 2701 private static final String TAG = "AvrcpMediaRsp"; 2702 2703 @Override 2704 public void setAddrPlayerRsp(byte[] address, int rspStatus) { 2705 if (!setAddressedPlayerRspNative(address, rspStatus)) { 2706 Log.e(TAG, "setAddrPlayerRsp failed!"); 2707 } 2708 } 2709 2710 @Override 2711 public void setBrowsedPlayerRsp(byte[] address, int rspStatus, byte depth, int numItems, 2712 String[] textArray) { 2713 if (!setBrowsedPlayerRspNative(address, rspStatus, depth, numItems, textArray)) { 2714 Log.e(TAG, "setBrowsedPlayerRsp failed!"); 2715 } 2716 } 2717 2718 @Override 2719 public void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp rspObj) { 2720 if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) { 2721 if (!mediaPlayerListRspNative(address, rspStatus, sUIDCounter, rspObj.mItemType, 2722 rspObj.mNumItems, rspObj.mPlayerIds, rspObj.mPlayerTypes, 2723 rspObj.mPlayerSubTypes, rspObj.mPlayStatusValues, 2724 rspObj.mFeatureBitMaskValues, rspObj.mPlayerNameList)) { 2725 Log.e(TAG, "mediaPlayerListRsp failed!"); 2726 } 2727 } else { 2728 Log.e(TAG, "mediaPlayerListRsp: rspObj is null"); 2729 if (!mediaPlayerListRspNative(address, rspStatus, sUIDCounter, (byte) 0x00, 0, null, 2730 null, null, null, null, null)) { 2731 Log.e(TAG, "mediaPlayerListRsp failed!"); 2732 } 2733 } 2734 } 2735 2736 @Override 2737 public void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp rspObj) { 2738 if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) { 2739 if (!getFolderItemsRspNative(address, rspStatus, sUIDCounter, rspObj.mScope, 2740 rspObj.mNumItems, rspObj.mFolderTypes, rspObj.mPlayable, rspObj.mItemTypes, 2741 rspObj.mItemUid, rspObj.mDisplayNames, rspObj.mAttributesNum, 2742 rspObj.mAttrIds, rspObj.mAttrValues)) { 2743 Log.e(TAG, "getFolderItemsRspNative failed!"); 2744 } 2745 } else { 2746 Log.e(TAG, "folderItemsRsp: rspObj is null or rspStatus is error:" + rspStatus); 2747 if (!getFolderItemsRspNative(address, rspStatus, sUIDCounter, (byte) 0x00, 0, null, 2748 null, null, null, null, null, null, null)) { 2749 Log.e(TAG, "getFolderItemsRspNative failed!"); 2750 } 2751 } 2752 2753 } 2754 2755 @Override 2756 public void changePathRsp(byte[] address, int rspStatus, int numItems) { 2757 if (!changePathRspNative(address, rspStatus, numItems)) { 2758 Log.e(TAG, "changePathRspNative failed!"); 2759 } 2760 } 2761 2762 @Override 2763 public void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp rspObj) { 2764 if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) { 2765 if (!getItemAttrRspNative(address, rspStatus, rspObj.mNumAttr, 2766 rspObj.mAttributesIds, rspObj.mAttributesArray)) { 2767 Log.e(TAG, "getItemAttrRspNative failed!"); 2768 } 2769 } else { 2770 Log.e(TAG, "getItemAttrRsp: rspObj is null or rspStatus is error:" + rspStatus); 2771 if (!getItemAttrRspNative(address, rspStatus, (byte) 0x00, null, null)) { 2772 Log.e(TAG, "getItemAttrRspNative failed!"); 2773 } 2774 } 2775 } 2776 2777 @Override 2778 public void playItemRsp(byte[] address, int rspStatus) { 2779 if (!playItemRspNative(address, rspStatus)) { 2780 Log.e(TAG, "playItemRspNative failed!"); 2781 } 2782 } 2783 2784 @Override 2785 public void getTotalNumOfItemsRsp(byte[] address, int rspStatus, int uidCounter, 2786 int numItems) { 2787 if (!getTotalNumOfItemsRspNative(address, rspStatus, sUIDCounter, numItems)) { 2788 Log.e(TAG, "getTotalNumOfItemsRspNative failed!"); 2789 } 2790 } 2791 2792 @Override 2793 public void addrPlayerChangedRsp(int type, int playerId, int uidCounter) { 2794 if (!registerNotificationRspAddrPlayerChangedNative(type, playerId, sUIDCounter)) { 2795 Log.e(TAG, "registerNotificationRspAddrPlayerChangedNative failed!"); 2796 } 2797 } 2798 2799 @Override 2800 public void avalPlayerChangedRsp(byte[] address, int type) { 2801 if (!registerNotificationRspAvalPlayerChangedNative(type)) { 2802 Log.e(TAG, "registerNotificationRspAvalPlayerChangedNative failed!"); 2803 } 2804 } 2805 2806 @Override 2807 public void uidsChangedRsp(int type) { 2808 if (!registerNotificationRspUIDsChangedNative(type, sUIDCounter)) { 2809 Log.e(TAG, "registerNotificationRspUIDsChangedNative failed!"); 2810 } 2811 } 2812 2813 @Override 2814 public void nowPlayingChangedRsp(int type) { 2815 if (mNowPlayingListChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) { 2816 if (DEBUG) { 2817 Log.d(TAG, "NowPlayingListChanged: Not registered or requesting."); 2818 } 2819 return; 2820 } 2821 2822 if (!registerNotificationRspNowPlayingChangedNative(type)) { 2823 Log.e(TAG, "registerNotificationRspNowPlayingChangedNative failed!"); 2824 } 2825 mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED; 2826 } 2827 2828 @Override 2829 public void trackChangedRsp(int type, byte[] uid) { 2830 if (!registerNotificationRspTrackChangeNative(type, uid)) { 2831 Log.e(TAG, "registerNotificationRspTrackChangeNative failed!"); 2832 } 2833 } 2834 } 2835 2836 /* getters for some private variables */ 2837 public AvrcpBrowseManager getAvrcpBrowseManager() { 2838 return mAvrcpBrowseManager; 2839 } 2840 2841 /* PASSTHROUGH COMMAND MANAGEMENT */ 2842 2843 void handlePassthroughCmd(int op, int state) { 2844 int code = avrcpPassthroughToKeyCode(op); 2845 if (code == KeyEvent.KEYCODE_UNKNOWN) { 2846 Log.w(TAG, "Ignoring passthrough of unknown key " + op + " state " + state); 2847 return; 2848 } 2849 int action = KeyEvent.ACTION_DOWN; 2850 if (state == AvrcpConstants.KEY_STATE_RELEASE) { 2851 action = KeyEvent.ACTION_UP; 2852 } 2853 KeyEvent event = new KeyEvent(action, code); 2854 if (!KeyEvent.isMediaKey(code)) { 2855 Log.w(TAG, "Passthrough non-media key " + op + " (code " + code + ") state " + state); 2856 } 2857 2858 mMediaSessionManager.dispatchMediaKeyEvent(event); 2859 addKeyPending(event); 2860 } 2861 2862 private int avrcpPassthroughToKeyCode(int operation) { 2863 switch (operation) { 2864 case BluetoothAvrcp.PASSTHROUGH_ID_UP: 2865 return KeyEvent.KEYCODE_DPAD_UP; 2866 case BluetoothAvrcp.PASSTHROUGH_ID_DOWN: 2867 return KeyEvent.KEYCODE_DPAD_DOWN; 2868 case BluetoothAvrcp.PASSTHROUGH_ID_LEFT: 2869 return KeyEvent.KEYCODE_DPAD_LEFT; 2870 case BluetoothAvrcp.PASSTHROUGH_ID_RIGHT: 2871 return KeyEvent.KEYCODE_DPAD_RIGHT; 2872 case BluetoothAvrcp.PASSTHROUGH_ID_RIGHT_UP: 2873 return KeyEvent.KEYCODE_DPAD_UP_RIGHT; 2874 case BluetoothAvrcp.PASSTHROUGH_ID_RIGHT_DOWN: 2875 return KeyEvent.KEYCODE_DPAD_DOWN_RIGHT; 2876 case BluetoothAvrcp.PASSTHROUGH_ID_LEFT_UP: 2877 return KeyEvent.KEYCODE_DPAD_UP_LEFT; 2878 case BluetoothAvrcp.PASSTHROUGH_ID_LEFT_DOWN: 2879 return KeyEvent.KEYCODE_DPAD_DOWN_LEFT; 2880 case BluetoothAvrcp.PASSTHROUGH_ID_0: 2881 return KeyEvent.KEYCODE_NUMPAD_0; 2882 case BluetoothAvrcp.PASSTHROUGH_ID_1: 2883 return KeyEvent.KEYCODE_NUMPAD_1; 2884 case BluetoothAvrcp.PASSTHROUGH_ID_2: 2885 return KeyEvent.KEYCODE_NUMPAD_2; 2886 case BluetoothAvrcp.PASSTHROUGH_ID_3: 2887 return KeyEvent.KEYCODE_NUMPAD_3; 2888 case BluetoothAvrcp.PASSTHROUGH_ID_4: 2889 return KeyEvent.KEYCODE_NUMPAD_4; 2890 case BluetoothAvrcp.PASSTHROUGH_ID_5: 2891 return KeyEvent.KEYCODE_NUMPAD_5; 2892 case BluetoothAvrcp.PASSTHROUGH_ID_6: 2893 return KeyEvent.KEYCODE_NUMPAD_6; 2894 case BluetoothAvrcp.PASSTHROUGH_ID_7: 2895 return KeyEvent.KEYCODE_NUMPAD_7; 2896 case BluetoothAvrcp.PASSTHROUGH_ID_8: 2897 return KeyEvent.KEYCODE_NUMPAD_8; 2898 case BluetoothAvrcp.PASSTHROUGH_ID_9: 2899 return KeyEvent.KEYCODE_NUMPAD_9; 2900 case BluetoothAvrcp.PASSTHROUGH_ID_DOT: 2901 return KeyEvent.KEYCODE_NUMPAD_DOT; 2902 case BluetoothAvrcp.PASSTHROUGH_ID_ENTER: 2903 return KeyEvent.KEYCODE_NUMPAD_ENTER; 2904 case BluetoothAvrcp.PASSTHROUGH_ID_CLEAR: 2905 return KeyEvent.KEYCODE_CLEAR; 2906 case BluetoothAvrcp.PASSTHROUGH_ID_CHAN_UP: 2907 return KeyEvent.KEYCODE_CHANNEL_UP; 2908 case BluetoothAvrcp.PASSTHROUGH_ID_CHAN_DOWN: 2909 return KeyEvent.KEYCODE_CHANNEL_DOWN; 2910 case BluetoothAvrcp.PASSTHROUGH_ID_PREV_CHAN: 2911 return KeyEvent.KEYCODE_LAST_CHANNEL; 2912 case BluetoothAvrcp.PASSTHROUGH_ID_INPUT_SEL: 2913 return KeyEvent.KEYCODE_TV_INPUT; 2914 case BluetoothAvrcp.PASSTHROUGH_ID_DISP_INFO: 2915 return KeyEvent.KEYCODE_INFO; 2916 case BluetoothAvrcp.PASSTHROUGH_ID_HELP: 2917 return KeyEvent.KEYCODE_HELP; 2918 case BluetoothAvrcp.PASSTHROUGH_ID_PAGE_UP: 2919 return KeyEvent.KEYCODE_PAGE_UP; 2920 case BluetoothAvrcp.PASSTHROUGH_ID_PAGE_DOWN: 2921 return KeyEvent.KEYCODE_PAGE_DOWN; 2922 case BluetoothAvrcp.PASSTHROUGH_ID_POWER: 2923 return KeyEvent.KEYCODE_POWER; 2924 case BluetoothAvrcp.PASSTHROUGH_ID_VOL_UP: 2925 return KeyEvent.KEYCODE_VOLUME_UP; 2926 case BluetoothAvrcp.PASSTHROUGH_ID_VOL_DOWN: 2927 return KeyEvent.KEYCODE_VOLUME_DOWN; 2928 case BluetoothAvrcp.PASSTHROUGH_ID_MUTE: 2929 return KeyEvent.KEYCODE_MUTE; 2930 case BluetoothAvrcp.PASSTHROUGH_ID_PLAY: 2931 return KeyEvent.KEYCODE_MEDIA_PLAY; 2932 case BluetoothAvrcp.PASSTHROUGH_ID_STOP: 2933 return KeyEvent.KEYCODE_MEDIA_STOP; 2934 case BluetoothAvrcp.PASSTHROUGH_ID_PAUSE: 2935 return KeyEvent.KEYCODE_MEDIA_PAUSE; 2936 case BluetoothAvrcp.PASSTHROUGH_ID_RECORD: 2937 return KeyEvent.KEYCODE_MEDIA_RECORD; 2938 case BluetoothAvrcp.PASSTHROUGH_ID_REWIND: 2939 return KeyEvent.KEYCODE_MEDIA_REWIND; 2940 case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR: 2941 return KeyEvent.KEYCODE_MEDIA_FAST_FORWARD; 2942 case BluetoothAvrcp.PASSTHROUGH_ID_EJECT: 2943 return KeyEvent.KEYCODE_MEDIA_EJECT; 2944 case BluetoothAvrcp.PASSTHROUGH_ID_FORWARD: 2945 return KeyEvent.KEYCODE_MEDIA_NEXT; 2946 case BluetoothAvrcp.PASSTHROUGH_ID_BACKWARD: 2947 return KeyEvent.KEYCODE_MEDIA_PREVIOUS; 2948 case BluetoothAvrcp.PASSTHROUGH_ID_F1: 2949 return KeyEvent.KEYCODE_F1; 2950 case BluetoothAvrcp.PASSTHROUGH_ID_F2: 2951 return KeyEvent.KEYCODE_F2; 2952 case BluetoothAvrcp.PASSTHROUGH_ID_F3: 2953 return KeyEvent.KEYCODE_F3; 2954 case BluetoothAvrcp.PASSTHROUGH_ID_F4: 2955 return KeyEvent.KEYCODE_F4; 2956 case BluetoothAvrcp.PASSTHROUGH_ID_F5: 2957 return KeyEvent.KEYCODE_F5; 2958 // Fallthrough for all unknown key mappings 2959 case BluetoothAvrcp.PASSTHROUGH_ID_SELECT: 2960 case BluetoothAvrcp.PASSTHROUGH_ID_ROOT_MENU: 2961 case BluetoothAvrcp.PASSTHROUGH_ID_SETUP_MENU: 2962 case BluetoothAvrcp.PASSTHROUGH_ID_CONT_MENU: 2963 case BluetoothAvrcp.PASSTHROUGH_ID_FAV_MENU: 2964 case BluetoothAvrcp.PASSTHROUGH_ID_EXIT: 2965 case BluetoothAvrcp.PASSTHROUGH_ID_SOUND_SEL: 2966 case BluetoothAvrcp.PASSTHROUGH_ID_ANGLE: 2967 case BluetoothAvrcp.PASSTHROUGH_ID_SUBPICT: 2968 case BluetoothAvrcp.PASSTHROUGH_ID_VENDOR: 2969 default: 2970 return KeyEvent.KEYCODE_UNKNOWN; 2971 } 2972 } 2973 2974 private void addKeyPending(KeyEvent event) { 2975 mPassthroughPending.add(new MediaKeyLog(System.currentTimeMillis(), event)); 2976 } 2977 2978 private void recordKeyDispatched(KeyEvent event, String packageName) { 2979 long time = System.currentTimeMillis(); 2980 Log.v(TAG, "recordKeyDispatched: " + event + " dispatched to " + packageName); 2981 setAddressedMediaSessionPackage(packageName); 2982 synchronized (mPassthroughPending) { 2983 Iterator<MediaKeyLog> pending = mPassthroughPending.iterator(); 2984 while (pending.hasNext()) { 2985 MediaKeyLog log = pending.next(); 2986 if (log.addDispatch(time, event, packageName)) { 2987 mPassthroughDispatched++; 2988 mPassthroughLogs.add(log); 2989 pending.remove(); 2990 return; 2991 } 2992 } 2993 Log.w(TAG, "recordKeyDispatch: can't find matching log!"); 2994 } 2995 } 2996 2997 private final MediaSessionManager.Callback mButtonDispatchCallback = 2998 new MediaSessionManager.Callback() { 2999 @Override 3000 public void onMediaKeyEventDispatched(KeyEvent event, MediaSession.Token token) { 3001 // Get the package name 3002 android.media.session.MediaController controller = 3003 new android.media.session.MediaController(mContext, token); 3004 String targetPackage = controller.getPackageName(); 3005 recordKeyDispatched(event, targetPackage); 3006 } 3007 3008 @Override 3009 public void onMediaKeyEventDispatched(KeyEvent event, ComponentName receiver) { 3010 recordKeyDispatched(event, receiver.getPackageName()); 3011 } 3012 3013 @Override 3014 public void onAddressedPlayerChanged(MediaSession.Token token) { 3015 setActiveMediaSession(token); 3016 } 3017 3018 @Override 3019 public void onAddressedPlayerChanged(ComponentName receiver) { 3020 if (receiver == null) { 3021 // No active sessions, and no session to revive, give up. 3022 setAddressedMediaSessionPackage(null); 3023 return; 3024 } 3025 // We can still get a passthrough which will revive this player. 3026 setAddressedMediaSessionPackage(receiver.getPackageName()); 3027 } 3028 }; 3029 3030 // Do not modify without updating the HAL bt_rc.h files. 3031 3032 // match up with btrc_play_status_t enum of bt_rc.h 3033 static final byte PLAYSTATUS_STOPPED = 0; 3034 static final byte PLAYSTATUS_PLAYING = 1; 3035 static final byte PLAYSTATUS_PAUSED = 2; 3036 static final byte PLAYSTATUS_FWD_SEEK = 3; 3037 static final byte PLAYSTATUS_REV_SEEK = 4; 3038 static final byte PLAYSTATUS_ERROR = (byte) 255; 3039 3040 // match up with btrc_media_attr_t enum of bt_rc.h 3041 static final int MEDIA_ATTR_TITLE = 1; 3042 static final int MEDIA_ATTR_ARTIST = 2; 3043 static final int MEDIA_ATTR_ALBUM = 3; 3044 static final int MEDIA_ATTR_TRACK_NUM = 4; 3045 static final int MEDIA_ATTR_NUM_TRACKS = 5; 3046 static final int MEDIA_ATTR_GENRE = 6; 3047 static final int MEDIA_ATTR_PLAYING_TIME = 7; 3048 3049 // match up with btrc_event_id_t enum of bt_rc.h 3050 static final int EVT_PLAY_STATUS_CHANGED = 1; 3051 static final int EVT_TRACK_CHANGED = 2; 3052 static final int EVT_TRACK_REACHED_END = 3; 3053 static final int EVT_TRACK_REACHED_START = 4; 3054 static final int EVT_PLAY_POS_CHANGED = 5; 3055 static final int EVT_BATT_STATUS_CHANGED = 6; 3056 static final int EVT_SYSTEM_STATUS_CHANGED = 7; 3057 static final int EVT_APP_SETTINGS_CHANGED = 8; 3058 static final int EVENT_NOW_PLAYING_CONTENT_CHANGED = 9; 3059 static final int EVT_AVBL_PLAYERS_CHANGED = 0xa; 3060 static final int EVT_ADDR_PLAYER_CHANGED = 0xb; 3061 static final int EVENT_UIDS_CHANGED = 0x0c; 3062 3063 private static native void classInitNative(); 3064 3065 private native void initNative(); 3066 3067 private native void cleanupNative(); 3068 3069 private native boolean getPlayStatusRspNative(byte[] address, int playStatus, int songLen, 3070 int songPos); 3071 3072 private native boolean getElementAttrRspNative(byte[] address, byte numAttr, int[] attrIds, 3073 String[] textArray); 3074 3075 private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus); 3076 3077 private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track); 3078 3079 private native boolean registerNotificationRspPlayPosNative(int type, int playPos); 3080 3081 private native boolean setVolumeNative(int volume); 3082 3083 private native boolean sendPassThroughCommandNative(int keyCode, int keyState); 3084 3085 private native boolean setAddressedPlayerRspNative(byte[] address, int rspStatus); 3086 3087 private native boolean setBrowsedPlayerRspNative(byte[] address, int rspStatus, byte depth, 3088 int numItems, String[] textArray); 3089 3090 private native boolean mediaPlayerListRspNative(byte[] address, int rsStatus, int uidCounter, 3091 byte itemType, int numItems, int[] playerIds, byte[] playerTypes, int[] playerSubTypes, 3092 byte[] playStatusValues, short[] featureBitMaskValues, String[] textArray); 3093 3094 private native boolean getFolderItemsRspNative(byte[] address, int rspStatus, short uidCounter, 3095 byte scope, int numItems, byte[] folderTypes, byte[] playable, byte[] itemTypes, 3096 byte[] itemUidArray, String[] textArray, int[] attributesNum, int[] attributesIds, 3097 String[] attributesArray); 3098 3099 private native boolean changePathRspNative(byte[] address, int rspStatus, int numItems); 3100 3101 private native boolean getItemAttrRspNative(byte[] address, int rspStatus, byte numAttr, 3102 int[] attrIds, String[] textArray); 3103 3104 private native boolean playItemRspNative(byte[] address, int rspStatus); 3105 3106 private native boolean getTotalNumOfItemsRspNative(byte[] address, int rspStatus, 3107 int uidCounter, int numItems); 3108 3109 private native boolean searchRspNative(byte[] address, int rspStatus, int uidCounter, 3110 int numItems); 3111 3112 private native boolean addToNowPlayingRspNative(byte[] address, int rspStatus); 3113 3114 private native boolean registerNotificationRspAddrPlayerChangedNative(int type, int playerId, 3115 int uidCounter); 3116 3117 private native boolean registerNotificationRspAvalPlayerChangedNative(int type); 3118 3119 private native boolean registerNotificationRspUIDsChangedNative(int type, int uidCounter); 3120 3121 private native boolean registerNotificationRspNowPlayingChangedNative(int type); 3122 3123} 3124