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