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