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