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