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