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