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