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