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