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