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