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