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