• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 java.util.Timer;
20 import java.util.TimerTask;
21 
22 import android.bluetooth.BluetoothA2dp;
23 import android.bluetooth.BluetoothAvrcp;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.res.Resources;
27 import android.content.SharedPreferences;
28 import android.graphics.Bitmap;
29 import android.media.AudioManager;
30 import android.media.MediaDescription;
31 import android.media.MediaMetadata;
32 import android.media.session.MediaController;
33 import android.media.session.MediaSessionManager;
34 import android.media.session.PlaybackState;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.HandlerThread;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.ParcelUuid;
41 import android.os.PowerManager;
42 import android.os.PowerManager.WakeLock;
43 import android.os.RemoteException;
44 import android.os.ServiceManager;
45 import android.os.SystemClock;
46 import android.util.Log;
47 import android.view.KeyEvent;
48 
49 import com.android.bluetooth.R;
50 import com.android.bluetooth.btservice.AdapterService;
51 import com.android.bluetooth.btservice.ProfileService;
52 import com.android.bluetooth.Utils;
53 import com.android.internal.util.IState;
54 import com.android.internal.util.State;
55 import com.android.internal.util.StateMachine;
56 
57 import java.lang.ref.WeakReference;
58 import java.util.ArrayList;
59 import java.util.HashMap;
60 import java.util.List;
61 import java.util.Set;
62 /**
63  * support Bluetooth AVRCP profile.
64  * support metadata, play status and event notification
65  */
66 public final class Avrcp {
67     private static final boolean DEBUG = false;
68     private static final String TAG = "Avrcp";
69     private static final String ABSOLUTE_VOLUME_BLACKLIST = "absolute_volume_blacklist";
70 
71     private Context mContext;
72     private final AudioManager mAudioManager;
73     private AvrcpMessageHandler mHandler;
74     private MediaSessionManager mMediaSessionManager;
75     private MediaSessionChangeListener mSessionChangeListener;
76     private MediaController mMediaController;
77     private MediaControllerListener mMediaControllerCb;
78     private MediaAttributes mMediaAttributes;
79     private int mTransportControlFlags;
80     private PlaybackState mCurrentPlayState;
81     private long mLastStateUpdate;
82     private int mPlayStatusChangedNT;
83     private int mTrackChangedNT;
84     private int mPlayPosChangedNT;
85     private long mTrackNumber;
86     private long mSongLengthMs;
87     private long mPlaybackIntervalMs;
88     private long mLastReportedPosition;
89     private long mNextPosMs;
90     private long mPrevPosMs;
91     private long mSkipStartTime;
92     private int mFeatures;
93     private int mRemoteVolume;
94     private int mLastRemoteVolume;
95     private int mInitialRemoteVolume;
96 
97     /* Local volume in audio index 0-15 */
98     private int mLocalVolume;
99     private int mLastLocalVolume;
100     private int mAbsVolThreshold;
101 
102     private String mAddress;
103     private HashMap<Integer, Integer> mVolumeMapping;
104 
105     private int mLastDirection;
106     private final int mVolumeStep;
107     private final int mAudioStreamMax;
108     private boolean mVolCmdAdjustInProgress;
109     private boolean mVolCmdSetInProgress;
110     private int mAbsVolRetryTimes;
111     private int mSkipAmount;
112 
113     /* BTRC features */
114     public static final int BTRC_FEAT_METADATA = 0x01;
115     public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
116     public static final int BTRC_FEAT_BROWSE = 0x04;
117 
118     /* AVRC response codes, from avrc_defs */
119     private static final int AVRC_RSP_NOT_IMPL = 8;
120     private static final int AVRC_RSP_ACCEPT = 9;
121     private static final int AVRC_RSP_REJ = 10;
122     private static final int AVRC_RSP_IN_TRANS = 11;
123     private static final int AVRC_RSP_IMPL_STBL = 12;
124     private static final int AVRC_RSP_CHANGED = 13;
125     private static final int AVRC_RSP_INTERIM = 15;
126 
127     private static final int MESSAGE_GET_RC_FEATURES = 1;
128     private static final int MESSAGE_GET_PLAY_STATUS = 2;
129     private static final int MESSAGE_GET_ELEM_ATTRS = 3;
130     private static final int MESSAGE_REGISTER_NOTIFICATION = 4;
131     private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5;
132     private static final int MESSAGE_VOLUME_CHANGED = 6;
133     private static final int MESSAGE_ADJUST_VOLUME = 7;
134     private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8;
135     private static final int MESSAGE_ABS_VOL_TIMEOUT = 9;
136     private static final int MESSAGE_FAST_FORWARD = 10;
137     private static final int MESSAGE_REWIND = 11;
138     private static final int MESSAGE_CHANGE_PLAY_POS = 12;
139     private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13;
140 
141     private static final int BUTTON_TIMEOUT_TIME = 2000;
142     private static final int BASE_SKIP_AMOUNT = 2000;
143     private static final int KEY_STATE_PRESS = 1;
144     private static final int KEY_STATE_RELEASE = 0;
145     private static final int SKIP_PERIOD = 400;
146     private static final int SKIP_DOUBLE_INTERVAL = 3000;
147     private static final long MAX_MULTIPLIER_VALUE = 128L;
148     private static final int CMD_TIMEOUT_DELAY = 2000;
149     private static final int MAX_ERROR_RETRY_TIMES = 6;
150     private static final int AVRCP_MAX_VOL = 127;
151     private static final int AVRCP_BASE_VOLUME_STEP = 1;
152 
153     static {
classInitNative()154         classInitNative();
155     }
156 
Avrcp(Context context)157     private Avrcp(Context context) {
158         mMediaAttributes = new MediaAttributes(null);
159         mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
160         mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
161         mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
162         mTrackNumber = -1L;
163         mLastStateUpdate = -1L;
164         mSongLengthMs = 0L;
165         mPlaybackIntervalMs = 0L;
166         mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
167         mLastReportedPosition = -1;
168         mNextPosMs = -1;
169         mPrevPosMs = -1;
170         mFeatures = 0;
171         mRemoteVolume = -1;
172         mInitialRemoteVolume = -1;
173         mLastRemoteVolume = -1;
174         mLastDirection = 0;
175         mVolCmdAdjustInProgress = false;
176         mVolCmdSetInProgress = false;
177         mAbsVolRetryTimes = 0;
178         mLocalVolume = -1;
179         mLastLocalVolume = -1;
180         mAbsVolThreshold = 0;
181         mVolumeMapping = new HashMap<Integer, Integer>();
182 
183         mContext = context;
184 
185         initNative();
186 
187         mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
188         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
189         mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
190         mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax);
191         Resources resources = context.getResources();
192         if (resources != null) {
193             mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
194         }
195     }
196 
start()197     private void start() {
198         HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
199         thread.start();
200         Looper looper = thread.getLooper();
201         mHandler = new AvrcpMessageHandler(looper);
202 
203         mSessionChangeListener = new MediaSessionChangeListener();
204         mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionChangeListener, null, mHandler);
205         List<MediaController> sessions = mMediaSessionManager.getActiveSessions(null);
206         mMediaControllerCb = new MediaControllerListener();
207         if (sessions.size() > 0) {
208             updateCurrentMediaController(sessions.get(0));
209         }
210     }
211 
make(Context context)212     public static Avrcp make(Context context) {
213         if (DEBUG) Log.v(TAG, "make");
214         Avrcp ar = new Avrcp(context);
215         ar.start();
216         return ar;
217     }
218 
doQuit()219     public void doQuit() {
220         mHandler.removeCallbacksAndMessages(null);
221         Looper looper = mHandler.getLooper();
222         if (looper != null) {
223             looper.quit();
224         }
225         mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionChangeListener);
226     }
227 
cleanup()228     public void cleanup() {
229         cleanupNative();
230         if (mVolumeMapping != null)
231             mVolumeMapping.clear();
232     }
233 
234     private class MediaControllerListener extends MediaController.Callback {
235         @Override
onMetadataChanged(MediaMetadata metadata)236         public void onMetadataChanged(MediaMetadata metadata) {
237             Log.v(TAG, "MediaController metadata changed");
238             updateMetadata(metadata);
239         }
240 
241         @Override
onPlaybackStateChanged(PlaybackState state)242         public void onPlaybackStateChanged(PlaybackState state) {
243             Log.v(TAG, "MediaController playback changed: " + state.toString());
244             updatePlaybackState(state);
245         }
246 
247         @Override
onSessionDestroyed()248         public void onSessionDestroyed() {
249             Log.v(TAG, "MediaController session destroyed");
250         }
251     }
252 
253     private class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener {
MediaSessionChangeListener()254         public MediaSessionChangeListener() {
255         }
256 
257         @Override
onActiveSessionsChanged(List<MediaController> controllers)258         public void onActiveSessionsChanged(List<MediaController> controllers) {
259             Log.v(TAG, "Active sessions changed, " + controllers.size() + " sessions");
260             if (controllers.size() > 0) {
261                 updateCurrentMediaController(controllers.get(0));
262             }
263         }
264     }
265 
updateCurrentMediaController(MediaController controller)266     private void updateCurrentMediaController(MediaController controller) {
267         Log.v(TAG, "Updating media controller to " + controller);
268         if (mMediaController != null) {
269             mMediaController.unregisterCallback(mMediaControllerCb);
270         }
271         mMediaController = controller;
272         if (mMediaController == null) {
273             updateMetadata(null);
274             return;
275         }
276         mMediaController.registerCallback(mMediaControllerCb, mHandler);
277         updateMetadata(mMediaController.getMetadata());
278     }
279 
280     /** Handles Avrcp messages. */
281     private final class AvrcpMessageHandler extends Handler {
AvrcpMessageHandler(Looper looper)282         private AvrcpMessageHandler(Looper looper) {
283             super(looper);
284         }
285 
286         @Override
handleMessage(Message msg)287         public void handleMessage(Message msg) {
288             switch (msg.what) {
289             case MESSAGE_GET_RC_FEATURES:
290                 String address = (String) msg.obj;
291                 if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+
292                                                              ", features="+msg.arg1);
293                 mFeatures = msg.arg1;
294                 mFeatures = modifyRcFeatureFromBlacklist(mFeatures, address);
295                 mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported());
296                 mLastLocalVolume = -1;
297                 mRemoteVolume = -1;
298                 mLocalVolume = -1;
299                 mInitialRemoteVolume = -1;
300                 mAddress = address;
301                 if (mVolumeMapping != null)
302                     mVolumeMapping.clear();
303                 break;
304 
305             case MESSAGE_GET_PLAY_STATUS:
306                 if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
307                 getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState),
308                                        (int)mSongLengthMs, (int)getPlayPosition());
309                 break;
310 
311             case MESSAGE_GET_ELEM_ATTRS:
312                 String[] textArray;
313                 int[] attrIds;
314                 byte numAttr = (byte) msg.arg1;
315                 ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj;
316                 Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
317                 attrIds = new int[numAttr];
318                 textArray = new String[numAttr];
319                 for (int i = 0; i < numAttr; ++i) {
320                     attrIds[i] = attrList.get(i).intValue();
321                     textArray[i] = mMediaAttributes.getString(attrIds[i]);
322                     Log.v(TAG, "getAttributeString:attrId=" + attrIds[i] +
323                                " str=" + textArray[i]);
324                 }
325                 getElementAttrRspNative(numAttr, attrIds, textArray);
326                 break;
327 
328             case MESSAGE_REGISTER_NOTIFICATION:
329                 if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
330                                       " param=" + msg.arg2);
331                 processRegisterNotification(msg.arg1, msg.arg2);
332                 break;
333 
334             case MESSAGE_PLAY_INTERVAL_TIMEOUT:
335                 if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
336                 sendPlayPosNotificationRsp(false);
337                 break;
338 
339             case MESSAGE_VOLUME_CHANGED:
340                 if (!isAbsoluteVolumeSupported()) {
341                     if (DEBUG) Log.v(TAG, "ignore MESSAGE_VOLUME_CHANGED");
342                     break;
343                 }
344 
345                 if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f)
346                                                         + " ctype=" + msg.arg2);
347 
348 
349                 boolean volAdj = false;
350                 if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) {
351                     if (mVolCmdAdjustInProgress == false && mVolCmdSetInProgress == false) {
352                         Log.e(TAG, "Unsolicited response, ignored");
353                         break;
354                     }
355                     removeMessages(MESSAGE_ABS_VOL_TIMEOUT);
356 
357                     volAdj = mVolCmdAdjustInProgress;
358                     mVolCmdAdjustInProgress = false;
359                     mVolCmdSetInProgress = false;
360                     mAbsVolRetryTimes = 0;
361                 }
362 
363                 byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD
364                 // convert remote volume to local volume
365                 int volIndex = convertToAudioStreamVolume(absVol);
366                 if (mInitialRemoteVolume == -1) {
367                     mInitialRemoteVolume = absVol;
368                     if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax && volIndex > mAbsVolThreshold) {
369                         if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" + mAbsVolThreshold);
370                         Message msg1 = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, mAbsVolThreshold , 0);
371                         mHandler.sendMessage(msg1);
372                         mRemoteVolume = absVol;
373                         mLocalVolume = volIndex;
374                         break;
375                     }
376                 }
377 
378                 if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT ||
379                                                  msg.arg2 == AVRC_RSP_CHANGED ||
380                                                  msg.arg2 == AVRC_RSP_INTERIM)) {
381                     /* If the volume has successfully changed */
382                     mLocalVolume = volIndex;
383                     if (mLastLocalVolume != -1 && msg.arg2 == AVRC_RSP_ACCEPT) {
384                         if (mLastLocalVolume != volIndex) {
385                             /* remote volume changed more than requested due to
386                              * local and remote has different volume steps */
387                             if (DEBUG) Log.d(TAG, "Remote returned volume does not match desired volume "
388                                 + mLastLocalVolume + " vs "
389                                 + volIndex);
390                             mLastLocalVolume = mLocalVolume;
391                         }
392                     }
393                     // remember the remote volume value, as it's the one supported by remote
394                     if (volAdj) {
395                         synchronized (mVolumeMapping) {
396                             mVolumeMapping.put(volIndex, (int)absVol);
397                             if (DEBUG) Log.v(TAG, "remember volume mapping " +volIndex+ "-"+absVol);
398                         }
399                     }
400 
401                     notifyVolumeChanged(mLocalVolume);
402                     mRemoteVolume = absVol;
403                     long pecentVolChanged = ((long)absVol * 100) / 0x7f;
404                     Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%");
405                 } else if (msg.arg2 == AVRC_RSP_REJ) {
406                     Log.e(TAG, "setAbsoluteVolume call rejected");
407                 } else if (volAdj && mLastRemoteVolume > 0 && mLastRemoteVolume < AVRCP_MAX_VOL &&
408                         mLocalVolume == volIndex &&
409                         (msg.arg2 == AVRC_RSP_ACCEPT )) {
410                     /* oops, the volume is still same, remote does not like the value
411                      * retry a volume one step up/down */
412                     if (DEBUG) Log.d(TAG, "Remote device didn't tune volume, let's try one more step.");
413                     int retry_volume = Math.min(AVRCP_MAX_VOL,
414                             Math.max(0, mLastRemoteVolume + mLastDirection));
415                     if (setVolumeNative(retry_volume)) {
416                         mLastRemoteVolume = retry_volume;
417                         sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
418                                            CMD_TIMEOUT_DELAY);
419                         mVolCmdAdjustInProgress = true;
420                     }
421                 }
422                 break;
423 
424             case MESSAGE_ADJUST_VOLUME:
425                 if (!isAbsoluteVolumeSupported()) {
426                     if (DEBUG) Log.v(TAG, "ignore MESSAGE_ADJUST_VOLUME");
427                     break;
428                 }
429 
430                 if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1);
431 
432                 if (mVolCmdAdjustInProgress || mVolCmdSetInProgress) {
433                     if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
434                     break;
435                 }
436 
437                 // Remote device didn't set initial volume. Let's black list it
438                 if (mInitialRemoteVolume == -1) {
439                     Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
440                     blackListCurrentDevice();
441                     break;
442                 }
443 
444                 // Wait on verification on volume from device, before changing the volume.
445                 if (mRemoteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) {
446                     int setVol = -1;
447                     int targetVolIndex = -1;
448                     if (mLocalVolume == 0 && msg.arg1 == -1) {
449                         if (DEBUG) Log.w(TAG, "No need to Vol down from 0.");
450                         break;
451                     }
452                     if (mLocalVolume == mAudioStreamMax && msg.arg1 == 1) {
453                         if (DEBUG) Log.w(TAG, "No need to Vol up from max.");
454                         break;
455                     }
456 
457                     targetVolIndex = mLocalVolume + msg.arg1;
458                     if (DEBUG) Log.d(TAG, "Adjusting volume to  " + targetVolIndex);
459 
460                     Integer i;
461                     synchronized (mVolumeMapping) {
462                         i = mVolumeMapping.get(targetVolIndex);
463                     }
464 
465                     if (i != null) {
466                         /* if we already know this volume mapping, use it */
467                         setVol = i.byteValue();
468                         if (setVol == mRemoteVolume) {
469                             if (DEBUG) Log.d(TAG, "got same volume from mapping for " + targetVolIndex + ", ignore.");
470                             setVol = -1;
471                         }
472                         if (DEBUG) Log.d(TAG, "set volume from mapping " + targetVolIndex + "-" + setVol);
473                     }
474 
475                     if (setVol == -1) {
476                         /* otherwise use phone steps */
477                         setVol = Math.min(AVRCP_MAX_VOL,
478                                  convertToAvrcpVolume(Math.max(0, targetVolIndex)));
479                         if (DEBUG) Log.d(TAG, "set volume from local volume "+ targetVolIndex+"-"+ setVol);
480                     }
481 
482                     if (setVolumeNative(setVol)) {
483                         sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
484                                            CMD_TIMEOUT_DELAY);
485                         mVolCmdAdjustInProgress = true;
486                         mLastDirection = msg.arg1;
487                         mLastRemoteVolume = setVol;
488                         mLastLocalVolume = targetVolIndex;
489                     } else {
490                          if (DEBUG) Log.d(TAG, "setVolumeNative failed");
491                     }
492                 } else {
493                     Log.e(TAG, "Unknown direction in MESSAGE_ADJUST_VOLUME");
494                 }
495                 break;
496 
497             case MESSAGE_SET_ABSOLUTE_VOLUME:
498                 if (!isAbsoluteVolumeSupported()) {
499                     if (DEBUG) Log.v(TAG, "ignore MESSAGE_SET_ABSOLUTE_VOLUME");
500                     break;
501                 }
502 
503                 if (DEBUG) Log.v(TAG, "MESSAGE_SET_ABSOLUTE_VOLUME");
504 
505                 if (mVolCmdSetInProgress || mVolCmdAdjustInProgress) {
506                     if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
507                     break;
508                 }
509 
510                 // Remote device didn't set initial volume. Let's black list it
511                 if (mInitialRemoteVolume == -1) {
512                     if (DEBUG) Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
513                     blackListCurrentDevice();
514                     break;
515                 }
516 
517                 int avrcpVolume = convertToAvrcpVolume(msg.arg1);
518                 avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
519                 if (DEBUG) Log.d(TAG, "Setting volume to " + msg.arg1 + "-" + avrcpVolume);
520                 if (setVolumeNative(avrcpVolume)) {
521                     sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
522                     mVolCmdSetInProgress = true;
523                     mLastRemoteVolume = avrcpVolume;
524                     mLastLocalVolume = msg.arg1;
525                 } else {
526                      if (DEBUG) Log.d(TAG, "setVolumeNative failed");
527                 }
528                 break;
529 
530             case MESSAGE_ABS_VOL_TIMEOUT:
531                 if (DEBUG) Log.v(TAG, "MESSAGE_ABS_VOL_TIMEOUT: Volume change cmd timed out.");
532                 mVolCmdAdjustInProgress = false;
533                 mVolCmdSetInProgress = false;
534                 if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
535                     mAbsVolRetryTimes = 0;
536                     /* too many volume change failures, black list the device */
537                     blackListCurrentDevice();
538                 } else {
539                     mAbsVolRetryTimes += 1;
540                     if (setVolumeNative(mLastRemoteVolume)) {
541                         sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
542                                            CMD_TIMEOUT_DELAY);
543                         mVolCmdSetInProgress = true;
544                     }
545                 }
546                 break;
547 
548             case MESSAGE_FAST_FORWARD:
549             case MESSAGE_REWIND:
550                 if (msg.what == MESSAGE_FAST_FORWARD) {
551                     if ((mCurrentPlayState.getActions() &
552                                 PlaybackState.ACTION_FAST_FORWARD) != 0) {
553                         int keyState = msg.arg1 == KEY_STATE_PRESS ?
554                                 KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
555                         KeyEvent keyEvent =
556                                 new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
557                         mMediaController.dispatchMediaButtonEvent(keyEvent);
558                         break;
559                     }
560                 } else if ((mCurrentPlayState.getActions() &
561                             PlaybackState.ACTION_REWIND) != 0) {
562                     int keyState = msg.arg1 == KEY_STATE_PRESS ?
563                             KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
564                     KeyEvent keyEvent =
565                             new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND);
566                     mMediaController.dispatchMediaButtonEvent(keyEvent);
567                     break;
568                 }
569 
570                 int skipAmount;
571                 if (msg.what == MESSAGE_FAST_FORWARD) {
572                     if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD");
573                     removeMessages(MESSAGE_FAST_FORWARD);
574                     skipAmount = BASE_SKIP_AMOUNT;
575                 } else {
576                     if (DEBUG) Log.v(TAG, "MESSAGE_REWIND");
577                     removeMessages(MESSAGE_REWIND);
578                     skipAmount = -BASE_SKIP_AMOUNT;
579                 }
580 
581                 if (hasMessages(MESSAGE_CHANGE_PLAY_POS) &&
582                         (skipAmount != mSkipAmount)) {
583                     Log.w(TAG, "missing release button event:" + mSkipAmount);
584                 }
585 
586                 if ((!hasMessages(MESSAGE_CHANGE_PLAY_POS)) ||
587                         (skipAmount != mSkipAmount)) {
588                     mSkipStartTime = SystemClock.elapsedRealtime();
589                 }
590 
591                 removeMessages(MESSAGE_CHANGE_PLAY_POS);
592                 if (msg.arg1 == KEY_STATE_PRESS) {
593                     mSkipAmount = skipAmount;
594                     changePositionBy(mSkipAmount * getSkipMultiplier());
595                     Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
596                     posMsg.arg1 = 1;
597                     sendMessageDelayed(posMsg, SKIP_PERIOD);
598                 }
599 
600                 break;
601 
602             case MESSAGE_CHANGE_PLAY_POS:
603                 if (DEBUG) Log.v(TAG, "MESSAGE_CHANGE_PLAY_POS:" + msg.arg1);
604                 changePositionBy(mSkipAmount * getSkipMultiplier());
605                 if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) {
606                     Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
607                     posMsg.arg1 = msg.arg1 + 1;
608                     sendMessageDelayed(posMsg, SKIP_PERIOD);
609                 }
610                 break;
611 
612             case MESSAGE_SET_A2DP_AUDIO_STATE:
613                 if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1);
614                 updateA2dpAudioState(msg.arg1);
615                 break;
616             }
617         }
618     }
619 
updateA2dpAudioState(int state)620     private void updateA2dpAudioState(int state) {
621         boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
622         if (isPlaying != isPlayingState(mCurrentPlayState)) {
623             /* if a2dp is streaming, check to make sure music is active */
624             if (isPlaying && !mAudioManager.isMusicActive())
625                 return;
626             PlaybackState.Builder builder = new PlaybackState.Builder();
627             if (isPlaying) {
628                 builder.setState(PlaybackState.STATE_PLAYING,
629                                  PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
630             } else {
631                 builder.setState(PlaybackState.STATE_PAUSED,
632                                  PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
633             }
634             updatePlaybackState(builder.build());
635         }
636     }
637 
updatePlaybackState(PlaybackState state)638     private void updatePlaybackState(PlaybackState state) {
639         if (state == null) {
640           state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE,
641                          PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build();
642         }
643 
644         int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
645         int newPlayStatus = convertPlayStateToPlayStatus(state);
646 
647         if (DEBUG) {
648             Log.v(TAG, "updatePlaybackState (" + mPlayStatusChangedNT + "): "+
649                        "old=" + mCurrentPlayState + "(" + oldPlayStatus + "), "+
650                        "new=" + state + "(" + newPlayStatus + ")");
651         }
652 
653         mCurrentPlayState = state;
654         mLastStateUpdate = SystemClock.elapsedRealtime();
655 
656         sendPlayPosNotificationRsp(false);
657 
658         if (mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM &&
659             (oldPlayStatus != newPlayStatus)) {
660             mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
661             registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
662         }
663     }
664 
updateTransportControls(int transportControlFlags)665     private void updateTransportControls(int transportControlFlags) {
666         mTransportControlFlags = transportControlFlags;
667     }
668 
669     class MediaAttributes {
670         private boolean exists;
671         private String title;
672         private String artistName;
673         private String albumName;
674         private String mediaNumber;
675         private String mediaTotalNumber;
676         private String genre;
677         private String playingTimeMs;
678 
679         private static final int ATTR_TITLE = 1;
680         private static final int ATTR_ARTIST_NAME = 2;
681         private static final int ATTR_ALBUM_NAME = 3;
682         private static final int ATTR_MEDIA_NUMBER = 4;
683         private static final int ATTR_MEDIA_TOTAL_NUMBER = 5;
684         private static final int ATTR_GENRE = 6;
685         private static final int ATTR_PLAYING_TIME_MS = 7;
686 
687 
MediaAttributes(MediaMetadata data)688         public MediaAttributes(MediaMetadata data) {
689             exists = data != null;
690             if (!exists)
691                 return;
692 
693             artistName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ARTIST));
694             albumName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ALBUM));
695             mediaNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
696             mediaTotalNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
697             genre = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_GENRE));
698             playingTimeMs = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_DURATION));
699 
700             // Try harder for the title.
701             title = data.getString(MediaMetadata.METADATA_KEY_TITLE);
702 
703             if (title == null) {
704                 MediaDescription desc = data.getDescription();
705                 if (desc != null) {
706                     CharSequence val = desc.getDescription();
707                     if (val != null)
708                         title = val.toString();
709                 }
710             }
711 
712             if (title == null)
713                 title = new String();
714         }
715 
equals(MediaAttributes other)716         public boolean equals(MediaAttributes other) {
717             if (other == null)
718                 return false;
719 
720             if (exists != other.exists)
721                 return false;
722 
723             if (exists == false)
724                 return true;
725 
726             return (title.equals(other.title)) &&
727                 (artistName.equals(other.artistName)) &&
728                 (albumName.equals(other.albumName)) &&
729                 (mediaNumber.equals(other.mediaNumber)) &&
730                 (mediaTotalNumber.equals(other.mediaTotalNumber)) &&
731                 (genre.equals(other.genre)) &&
732                 (playingTimeMs.equals(other.playingTimeMs));
733         }
734 
getString(int attrId)735         public String getString(int attrId) {
736             if (!exists)
737                 return new String();
738 
739             switch (attrId) {
740                 case ATTR_TITLE:
741                     return title;
742                 case ATTR_ARTIST_NAME:
743                     return artistName;
744                 case ATTR_ALBUM_NAME:
745                     return albumName;
746                 case ATTR_MEDIA_NUMBER:
747                     return mediaNumber;
748                 case ATTR_MEDIA_TOTAL_NUMBER:
749                     return mediaTotalNumber;
750                 case ATTR_GENRE:
751                     return genre;
752                 case ATTR_PLAYING_TIME_MS:
753                     return playingTimeMs;
754                 default:
755                     return new String();
756             }
757         }
758 
stringOrBlank(String s)759         private String stringOrBlank(String s) {
760             return s == null ? new String() : s;
761         }
762 
longStringOrBlank(Long s)763         private String longStringOrBlank(Long s) {
764             return s == null ? new String() : s.toString();
765         }
766 
toString()767         public String toString() {
768             if (!exists)
769                 return "[MediaAttributes: none]";
770 
771             return "[MediaAttributes: " + title + " - " + albumName + " by "
772                 + artistName + " (" + mediaNumber + "/" + mediaTotalNumber + ") "
773                 + genre + "]";
774         }
775     }
776 
updateMetadata(MediaMetadata data)777     private void updateMetadata(MediaMetadata data) {
778         MediaAttributes oldAttributes = mMediaAttributes;
779         mMediaAttributes = new MediaAttributes(data);
780         if (data == null) {
781             mSongLengthMs = 0L;
782         } else {
783             mSongLengthMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION);
784         }
785         if (!oldAttributes.equals(mMediaAttributes)) {
786             Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString());
787             mTrackNumber++;
788 
789             if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
790                 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
791                 sendTrackChangedRsp();
792             }
793         } else {
794             Log.v(TAG, "Updated " + mMediaAttributes.toString() + " but no change!");
795         }
796 
797         // Update the play state, which sends play state and play position
798         // notifications if needed.
799         if (mMediaController != null) {
800           updatePlaybackState(mMediaController.getPlaybackState());
801         } else {
802           updatePlaybackState(null);
803         }
804     }
805 
getRcFeatures(byte[] address, int features)806     private void getRcFeatures(byte[] address, int features) {
807         Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0,
808                                              Utils.getAddressStringFromByte(address));
809         mHandler.sendMessage(msg);
810     }
811 
getPlayStatus()812     private void getPlayStatus() {
813         Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
814         mHandler.sendMessage(msg);
815     }
816 
getElementAttr(byte numAttr, int[] attrs)817     private void getElementAttr(byte numAttr, int[] attrs) {
818         int i;
819         ArrayList<Integer> attrList = new ArrayList<Integer>();
820         for (i = 0; i < numAttr; ++i) {
821             attrList.add(attrs[i]);
822         }
823         Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList);
824         mHandler.sendMessage(msg);
825     }
826 
registerNotification(int eventId, int param)827     private void registerNotification(int eventId, int param) {
828         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
829         mHandler.sendMessage(msg);
830     }
831 
processRegisterNotification(int eventId, int param)832     private void processRegisterNotification(int eventId, int param) {
833         switch (eventId) {
834             case EVT_PLAY_STATUS_CHANGED:
835                 mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
836                 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
837                         convertPlayStateToPlayStatus(mCurrentPlayState));
838                 break;
839 
840             case EVT_TRACK_CHANGED:
841                 mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
842                 sendTrackChangedRsp();
843                 break;
844 
845             case EVT_PLAY_POS_CHANGED:
846                 mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
847                 mPlaybackIntervalMs = (long)param * 1000L;
848                 sendPlayPosNotificationRsp(true);
849                 break;
850 
851         }
852     }
853 
handlePassthroughCmd(int id, int keyState)854     private void handlePassthroughCmd(int id, int keyState) {
855         switch (id) {
856             case BluetoothAvrcp.PASSTHROUGH_ID_REWIND:
857                 rewind(keyState);
858                 break;
859             case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR:
860                 fastForward(keyState);
861                 break;
862         }
863     }
864 
fastForward(int keyState)865     private void fastForward(int keyState) {
866         Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0);
867         mHandler.sendMessage(msg);
868     }
869 
rewind(int keyState)870     private void rewind(int keyState) {
871         Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0);
872         mHandler.sendMessage(msg);
873     }
874 
changePositionBy(long amount)875     private void changePositionBy(long amount) {
876         long currentPosMs = getPlayPosition();
877         if (currentPosMs == -1L) return;
878         long newPosMs = Math.max(0L, currentPosMs + amount);
879         mMediaController.getTransportControls().seekTo(newPosMs);
880     }
881 
getSkipMultiplier()882     private int getSkipMultiplier() {
883         long currentTime = SystemClock.elapsedRealtime();
884         long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
885         return (int) Math.min(MAX_MULTIPLIER_VALUE, multi);
886     }
887 
sendTrackChangedRsp()888     private void sendTrackChangedRsp() {
889         byte[] track = new byte[TRACK_ID_SIZE];
890 
891         /* If no track is currently selected, then return
892            0xFFFFFFFFFFFFFFFF in the interim response */
893         long trackNumberRsp = -1L;
894 
895         if (isPlayingState(mCurrentPlayState)) {
896             trackNumberRsp = mTrackNumber;
897         }
898 
899         /* track is stored in big endian format */
900         for (int i = 0; i < TRACK_ID_SIZE; ++i) {
901             track[i] = (byte) (trackNumberRsp >> (56 - 8 * i));
902         }
903         registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
904     }
905 
getPlayPosition()906     private long getPlayPosition() {
907         if (mCurrentPlayState == null)
908             return -1L;
909 
910         if (mCurrentPlayState.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN)
911             return -1L;
912 
913         if (isPlayingState(mCurrentPlayState)) {
914             return SystemClock.elapsedRealtime() - mLastStateUpdate + mCurrentPlayState.getPosition();
915         }
916 
917         return mCurrentPlayState.getPosition();
918     }
919 
convertPlayStateToPlayStatus(PlaybackState state)920     private int convertPlayStateToPlayStatus(PlaybackState state) {
921         int playStatus = PLAYSTATUS_ERROR;
922         switch (state.getState()) {
923             case PlaybackState.STATE_PLAYING:
924             case PlaybackState.STATE_BUFFERING:
925                 playStatus = PLAYSTATUS_PLAYING;
926                 break;
927 
928             case PlaybackState.STATE_STOPPED:
929             case PlaybackState.STATE_NONE:
930                 playStatus = PLAYSTATUS_STOPPED;
931                 break;
932 
933             case PlaybackState.STATE_PAUSED:
934                 playStatus = PLAYSTATUS_PAUSED;
935                 break;
936 
937             case PlaybackState.STATE_FAST_FORWARDING:
938             case PlaybackState.STATE_SKIPPING_TO_NEXT:
939             case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
940                 playStatus = PLAYSTATUS_FWD_SEEK;
941                 break;
942 
943             case PlaybackState.STATE_REWINDING:
944             case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
945                 playStatus = PLAYSTATUS_REV_SEEK;
946                 break;
947 
948             case PlaybackState.STATE_ERROR:
949                 playStatus = PLAYSTATUS_ERROR;
950                 break;
951 
952         }
953         return playStatus;
954     }
955 
isPlayingState(PlaybackState state)956     private boolean isPlayingState(PlaybackState state) {
957         return (state.getState() == PlaybackState.STATE_PLAYING) ||
958                 (state.getState() == PlaybackState.STATE_BUFFERING);
959     }
960 
961     /**
962      * Sends a play position notification, or schedules one to be
963      * sent later at an appropriate time. If |requested| is true,
964      * does both because this was called in reponse to a request from the
965      * TG.
966      */
sendPlayPosNotificationRsp(boolean requested)967     private void sendPlayPosNotificationRsp(boolean requested) {
968         if (!requested && mPlayPosChangedNT != NOTIFICATION_TYPE_INTERIM) {
969             if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: Not registered or requesting.");
970             return;
971         }
972 
973         long playPositionMs = getPlayPosition();
974 
975         // mNextPosMs is set to -1 when the previous position was invalid
976         // so this will be true if the new position is valid & old was invalid.
977         // mPlayPositionMs is set to -1 when the new position is invalid,
978         // and the old mPrevPosMs is >= 0 so this is true when the new is invalid
979         // and the old was valid.
980         if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: (" + requested + ") "
981             + mPrevPosMs + " <=? " + playPositionMs + " <=? " + mNextPosMs);
982         if (requested || ((mLastReportedPosition != playPositionMs) &&
983               (playPositionMs >= mNextPosMs) || (playPositionMs <= mPrevPosMs))) {
984             if (!requested) mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
985             registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPositionMs);
986             mLastReportedPosition = playPositionMs;
987             if (playPositionMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
988                 mNextPosMs = playPositionMs + mPlaybackIntervalMs;
989                 mPrevPosMs = playPositionMs - mPlaybackIntervalMs;
990             } else {
991                 mNextPosMs = -1;
992                 mPrevPosMs = -1;
993             }
994         }
995 
996         mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
997         if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM && isPlayingState(mCurrentPlayState)) {
998             Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
999             long delay = mPlaybackIntervalMs;
1000             if (mNextPosMs != -1) {
1001                 delay = mNextPosMs - (playPositionMs > 0 ? playPositionMs : 0);
1002             }
1003             if (DEBUG) Log.d(TAG, "PLAY_INTERVAL_TIMEOUT set for " + delay + "ms from now");
1004             mHandler.sendMessageDelayed(msg, delay);
1005         }
1006     }
1007 
1008     /**
1009      * This is called from AudioService. It will return whether this device supports abs volume.
1010      * NOT USED AT THE MOMENT.
1011      */
isAbsoluteVolumeSupported()1012     public boolean isAbsoluteVolumeSupported() {
1013         return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
1014     }
1015 
1016     /**
1017      * We get this call from AudioService. This will send a message to our handler object,
1018      * requesting our handler to call setVolumeNative()
1019      */
adjustVolume(int direction)1020     public void adjustVolume(int direction) {
1021         Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
1022         mHandler.sendMessage(msg);
1023     }
1024 
setAbsoluteVolume(int volume)1025     public void setAbsoluteVolume(int volume) {
1026         if (volume == mLocalVolume) {
1027             if (DEBUG) Log.v(TAG, "setAbsoluteVolume is setting same index, ignore "+volume);
1028             return;
1029         }
1030 
1031         mHandler.removeMessages(MESSAGE_ADJUST_VOLUME);
1032         Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, volume, 0);
1033         mHandler.sendMessage(msg);
1034     }
1035 
1036     /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
1037      * case when the volume is change locally on the carkit. This notification is not called when
1038      * the volume is changed from the phone.
1039      *
1040      * This method will send a message to our handler to change the local stored volume and notify
1041      * AudioService to update the UI
1042      */
volumeChangeCallback(int volume, int ctype)1043     private void volumeChangeCallback(int volume, int ctype) {
1044         Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype);
1045         mHandler.sendMessage(msg);
1046     }
1047 
notifyVolumeChanged(int volume)1048     private void notifyVolumeChanged(int volume) {
1049         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
1050                       AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
1051     }
1052 
convertToAudioStreamVolume(int volume)1053     private int convertToAudioStreamVolume(int volume) {
1054         // Rescale volume to match AudioSystem's volume
1055         return (int) Math.floor((double) volume*mAudioStreamMax/AVRCP_MAX_VOL);
1056     }
1057 
convertToAvrcpVolume(int volume)1058     private int convertToAvrcpVolume(int volume) {
1059         return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);
1060     }
1061 
blackListCurrentDevice()1062     private void blackListCurrentDevice() {
1063         mFeatures &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
1064         mAudioManager.avrcpSupportsAbsoluteVolume(mAddress, isAbsoluteVolumeSupported());
1065 
1066         SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1067                 Context.MODE_PRIVATE);
1068         SharedPreferences.Editor editor = pref.edit();
1069         editor.putBoolean(mAddress, true);
1070         editor.commit();
1071     }
1072 
modifyRcFeatureFromBlacklist(int feature, String address)1073     private int modifyRcFeatureFromBlacklist(int feature, String address) {
1074         SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1075                 Context.MODE_PRIVATE);
1076         if (!pref.contains(address)) {
1077             return feature;
1078         }
1079         if (pref.getBoolean(address, false)) {
1080             feature &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
1081         }
1082         return feature;
1083     }
1084 
resetBlackList(String address)1085     public void resetBlackList(String address) {
1086         SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1087                 Context.MODE_PRIVATE);
1088         SharedPreferences.Editor editor = pref.edit();
1089         editor.remove(address);
1090         editor.commit();
1091     }
1092 
1093     /**
1094      * This is called from A2dpStateMachine to set A2dp audio state.
1095      */
setA2dpAudioState(int state)1096     public void setA2dpAudioState(int state) {
1097         Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0);
1098         mHandler.sendMessage(msg);
1099     }
1100 
dump(StringBuilder sb)1101     public void dump(StringBuilder sb) {
1102         sb.append("AVRCP:\n");
1103         ProfileService.println(sb, "mMediaAttributes: " + mMediaAttributes);
1104         ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
1105         ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
1106         ProfileService.println(sb, "mLastStateUpdate: " + mLastStateUpdate);
1107         ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
1108         ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
1109         ProfileService.println(sb, "mTrackNumber: " + mTrackNumber);
1110         ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
1111         ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
1112         ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
1113         ProfileService.println(sb, "mNextPosMs: " + mNextPosMs);
1114         ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs);
1115         ProfileService.println(sb, "mSkipStartTime: " + mSkipStartTime);
1116         ProfileService.println(sb, "mFeatures: " + mFeatures);
1117         ProfileService.println(sb, "mRemoteVolume: " + mRemoteVolume);
1118         ProfileService.println(sb, "mLastRemoteVolume: " + mLastRemoteVolume);
1119         ProfileService.println(sb, "mLastDirection: " + mLastDirection);
1120         ProfileService.println(sb, "mVolumeStep: " + mVolumeStep);
1121         ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax);
1122         ProfileService.println(sb, "mVolCmdAdjustInProgress: " + mVolCmdAdjustInProgress);
1123         ProfileService.println(sb, "mVolCmdSetInProgress: " + mVolCmdSetInProgress);
1124         ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes);
1125         ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
1126         ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString());
1127         if (mMediaController != null)
1128             ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName());
1129     }
1130 
1131     // Do not modify without updating the HAL bt_rc.h files.
1132 
1133     // match up with btrc_play_status_t enum of bt_rc.h
1134     final static int PLAYSTATUS_STOPPED = 0;
1135     final static int PLAYSTATUS_PLAYING = 1;
1136     final static int PLAYSTATUS_PAUSED = 2;
1137     final static int PLAYSTATUS_FWD_SEEK = 3;
1138     final static int PLAYSTATUS_REV_SEEK = 4;
1139     final static int PLAYSTATUS_ERROR = 255;
1140 
1141     // match up with btrc_media_attr_t enum of bt_rc.h
1142     final static int MEDIA_ATTR_TITLE = 1;
1143     final static int MEDIA_ATTR_ARTIST = 2;
1144     final static int MEDIA_ATTR_ALBUM = 3;
1145     final static int MEDIA_ATTR_TRACK_NUM = 4;
1146     final static int MEDIA_ATTR_NUM_TRACKS = 5;
1147     final static int MEDIA_ATTR_GENRE = 6;
1148     final static int MEDIA_ATTR_PLAYING_TIME = 7;
1149 
1150     // match up with btrc_event_id_t enum of bt_rc.h
1151     final static int EVT_PLAY_STATUS_CHANGED = 1;
1152     final static int EVT_TRACK_CHANGED = 2;
1153     final static int EVT_TRACK_REACHED_END = 3;
1154     final static int EVT_TRACK_REACHED_START = 4;
1155     final static int EVT_PLAY_POS_CHANGED = 5;
1156     final static int EVT_BATT_STATUS_CHANGED = 6;
1157     final static int EVT_SYSTEM_STATUS_CHANGED = 7;
1158     final static int EVT_APP_SETTINGS_CHANGED = 8;
1159 
1160     // match up with btrc_notification_type_t enum of bt_rc.h
1161     final static int NOTIFICATION_TYPE_INTERIM = 0;
1162     final static int NOTIFICATION_TYPE_CHANGED = 1;
1163 
1164     // match up with BTRC_UID_SIZE of bt_rc.h
1165     final static int TRACK_ID_SIZE = 8;
1166 
classInitNative()1167     private native static void classInitNative();
initNative()1168     private native void initNative();
cleanupNative()1169     private native void cleanupNative();
getPlayStatusRspNative(int playStatus, int songLen, int songPos)1170     private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray)1171     private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
registerNotificationRspPlayStatusNative(int type, int playStatus)1172     private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
registerNotificationRspTrackChangeNative(int type, byte[] track)1173     private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
registerNotificationRspPlayPosNative(int type, int playPos)1174     private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
setVolumeNative(int volume)1175     private native boolean setVolumeNative(int volume);
sendPassThroughCommandNative(int keyCode, int keyState)1176     private native boolean sendPassThroughCommandNative(int keyCode, int keyState);
1177 
1178 }
1179