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