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