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.app.PendingIntent;
23 import android.bluetooth.BluetoothA2dp;
24 import android.bluetooth.BluetoothAvrcp;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.graphics.Bitmap;
28 import android.media.AudioManager;
29 import android.media.IRemoteControlDisplay;
30 import android.media.MediaMetadataRetriever;
31 import android.media.RemoteControlClient;
32 import android.media.RemoteController;
33 import android.media.RemoteController.MetadataEditor;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.ParcelUuid;
40 import android.os.PowerManager;
41 import android.os.PowerManager.WakeLock;
42 import android.os.RemoteException;
43 import android.os.ServiceManager;
44 import android.os.SystemClock;
45 import android.util.Log;
46 import android.view.KeyEvent;
47 
48 import com.android.bluetooth.btservice.AdapterService;
49 import com.android.bluetooth.btservice.ProfileService;
50 import com.android.bluetooth.Utils;
51 import com.android.internal.util.IState;
52 import com.android.internal.util.State;
53 import com.android.internal.util.StateMachine;
54 
55 import java.lang.ref.WeakReference;
56 import java.util.ArrayList;
57 import java.util.List;
58 import java.util.Set;
59 /**
60  * support Bluetooth AVRCP profile.
61  * support metadata, play status and event notification
62  */
63 public final class Avrcp {
64     private static final boolean DEBUG = false;
65     private static final String TAG = "Avrcp";
66 
67     private Context mContext;
68     private final AudioManager mAudioManager;
69     private AvrcpMessageHandler mHandler;
70     private RemoteController mRemoteController;
71     private RemoteControllerWeak mRemoteControllerCb;
72     private Metadata mMetadata;
73     private int mTransportControlFlags;
74     private int mCurrentPlayState;
75     private int mPlayStatusChangedNT;
76     private int mTrackChangedNT;
77     private long mTrackNumber;
78     private long mCurrentPosMs;
79     private long mPlayStartTimeMs;
80     private long mSongLengthMs;
81     private long mPlaybackIntervalMs;
82     private int mPlayPosChangedNT;
83     private long mNextPosMs;
84     private long mPrevPosMs;
85     private long mSkipStartTime;
86     private int mFeatures;
87     private int mAbsoluteVolume;
88     private int mLastSetVolume;
89     private int mLastDirection;
90     private final int mVolumeStep;
91     private final int mAudioStreamMax;
92     private boolean mVolCmdInProgress;
93     private int mAbsVolRetryTimes;
94     private int mSkipAmount;
95 
96     /* BTRC features */
97     public static final int BTRC_FEAT_METADATA = 0x01;
98     public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
99     public static final int BTRC_FEAT_BROWSE = 0x04;
100 
101     /* AVRC response codes, from avrc_defs */
102     private static final int AVRC_RSP_NOT_IMPL = 8;
103     private static final int AVRC_RSP_ACCEPT = 9;
104     private static final int AVRC_RSP_REJ = 10;
105     private static final int AVRC_RSP_IN_TRANS = 11;
106     private static final int AVRC_RSP_IMPL_STBL = 12;
107     private static final int AVRC_RSP_CHANGED = 13;
108     private static final int AVRC_RSP_INTERIM = 15;
109 
110     private static final int MESSAGE_GET_RC_FEATURES = 1;
111     private static final int MESSAGE_GET_PLAY_STATUS = 2;
112     private static final int MESSAGE_GET_ELEM_ATTRS = 3;
113     private static final int MESSAGE_REGISTER_NOTIFICATION = 4;
114     private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5;
115     private static final int MESSAGE_VOLUME_CHANGED = 6;
116     private static final int MESSAGE_ADJUST_VOLUME = 7;
117     private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8;
118     private static final int MESSAGE_ABS_VOL_TIMEOUT = 9;
119     private static final int MESSAGE_FAST_FORWARD = 10;
120     private static final int MESSAGE_REWIND = 11;
121     private static final int MESSAGE_CHANGE_PLAY_POS = 12;
122     private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13;
123     private static final int MSG_UPDATE_STATE = 100;
124     private static final int MSG_SET_METADATA = 101;
125     private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
126     private static final int MSG_SET_GENERATION_ID = 104;
127 
128     private static final int BUTTON_TIMEOUT_TIME = 2000;
129     private static final int BASE_SKIP_AMOUNT = 2000;
130     private static final int KEY_STATE_PRESS = 1;
131     private static final int KEY_STATE_RELEASE = 0;
132     private static final int SKIP_PERIOD = 400;
133     private static final int SKIP_DOUBLE_INTERVAL = 3000;
134     private static final long MAX_MULTIPLIER_VALUE = 128L;
135     private static final int CMD_TIMEOUT_DELAY = 2000;
136     private static final int MAX_ERROR_RETRY_TIMES = 3;
137     private static final int AVRCP_MAX_VOL = 127;
138     private static final int AVRCP_BASE_VOLUME_STEP = 1;
139 
140     static {
classInitNative()141         classInitNative();
142     }
143 
Avrcp(Context context)144     private Avrcp(Context context) {
145         mMetadata = new Metadata();
146         mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
147         mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
148         mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
149         mTrackNumber = -1L;
150         mCurrentPosMs = 0L;
151         mPlayStartTimeMs = -1L;
152         mSongLengthMs = 0L;
153         mPlaybackIntervalMs = 0L;
154         mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
155         mFeatures = 0;
156         mAbsoluteVolume = -1;
157         mLastSetVolume = -1;
158         mLastDirection = 0;
159         mVolCmdInProgress = false;
160         mAbsVolRetryTimes = 0;
161 
162         mContext = context;
163 
164         initNative();
165 
166         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
167         mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
168         mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax);
169     }
170 
start()171     private void start() {
172         HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
173         thread.start();
174         Looper looper = thread.getLooper();
175         mHandler = new AvrcpMessageHandler(looper);
176         mRemoteControllerCb = new RemoteControllerWeak(mHandler);
177         mRemoteController = new RemoteController(mContext, mRemoteControllerCb);
178         mAudioManager.registerRemoteController(mRemoteController);
179         mRemoteController.setSynchronizationMode(RemoteController.POSITION_SYNCHRONIZATION_CHECK);
180     }
181 
make(Context context)182     public static Avrcp make(Context context) {
183         if (DEBUG) Log.v(TAG, "make");
184         Avrcp ar = new Avrcp(context);
185         ar.start();
186         return ar;
187     }
188 
doQuit()189     public void doQuit() {
190         mHandler.removeCallbacksAndMessages(null);
191         Looper looper = mHandler.getLooper();
192         if (looper != null) {
193             looper.quit();
194         }
195         mAudioManager.unregisterRemoteController(mRemoteController);
196     }
197 
cleanup()198     public void cleanup() {
199         cleanupNative();
200     }
201 
202     private static class RemoteControllerWeak implements RemoteController.OnClientUpdateListener {
203         private final WeakReference<Handler> mLocalHandler;
204 
RemoteControllerWeak(Handler handler)205         public RemoteControllerWeak(Handler handler) {
206             mLocalHandler = new WeakReference<Handler>(handler);
207         }
208 
209         @Override
onClientChange(boolean clearing)210         public void onClientChange(boolean clearing) {
211             Handler handler = mLocalHandler.get();
212             if (handler != null) {
213                 handler.obtainMessage(MSG_SET_GENERATION_ID,
214                         0, (clearing ? 1 : 0), null).sendToTarget();
215             }
216         }
217 
218         @Override
onClientPlaybackStateUpdate(int state)219         public void onClientPlaybackStateUpdate(int state) {
220             // Should never be called with the existing code, but just in case
221             Handler handler = mLocalHandler.get();
222             if (handler != null) {
223                 handler.obtainMessage(MSG_UPDATE_STATE, 0, state,
224                         new Long(RemoteControlClient.PLAYBACK_POSITION_INVALID)).sendToTarget();
225             }
226         }
227 
228         @Override
onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, long currentPosMs, float speed)229         public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
230                 long currentPosMs, float speed) {
231             Handler handler = mLocalHandler.get();
232             if (handler != null) {
233                 handler.obtainMessage(MSG_UPDATE_STATE, 0, state,
234                         new Long(currentPosMs)).sendToTarget();
235             }
236         }
237 
238         @Override
onClientTransportControlUpdate(int transportControlFlags)239         public void onClientTransportControlUpdate(int transportControlFlags) {
240             Handler handler = mLocalHandler.get();
241             if (handler != null) {
242                 handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, 0, transportControlFlags)
243                         .sendToTarget();
244             }
245         }
246 
247         @Override
onClientMetadataUpdate(MetadataEditor metadataEditor)248         public void onClientMetadataUpdate(MetadataEditor metadataEditor) {
249             Handler handler = mLocalHandler.get();
250             if (handler != null) {
251                 handler.obtainMessage(MSG_SET_METADATA, 0, 0, metadataEditor).sendToTarget();
252             }
253         }
254     }
255 
256     /** Handles Avrcp messages. */
257     private final class AvrcpMessageHandler extends Handler {
AvrcpMessageHandler(Looper looper)258         private AvrcpMessageHandler(Looper looper) {
259             super(looper);
260         }
261 
262         @Override
handleMessage(Message msg)263         public void handleMessage(Message msg) {
264             switch (msg.what) {
265             case MSG_UPDATE_STATE:
266                     updatePlayPauseState(msg.arg2, ((Long) msg.obj).longValue());
267                 break;
268 
269             case MSG_SET_METADATA:
270                     updateMetadata((MetadataEditor) msg.obj);
271                 break;
272 
273             case MSG_SET_TRANSPORT_CONTROLS:
274                     updateTransportControls(msg.arg2);
275                 break;
276 
277             case MSG_SET_GENERATION_ID:
278                 if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
279                 break;
280 
281             case MESSAGE_GET_RC_FEATURES:
282                 String address = (String) msg.obj;
283                 if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+
284                                                              ", features="+msg.arg1);
285                 mFeatures = msg.arg1;
286                 mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported());
287                 break;
288 
289             case MESSAGE_GET_PLAY_STATUS:
290                 if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
291                 getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState),
292                                        (int)mSongLengthMs, (int)getPlayPosition());
293                 break;
294 
295             case MESSAGE_GET_ELEM_ATTRS:
296             {
297                 String[] textArray;
298                 int[] attrIds;
299                 byte numAttr = (byte) msg.arg1;
300                 ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj;
301                 if (DEBUG) Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
302                 attrIds = new int[numAttr];
303                 textArray = new String[numAttr];
304                 for (int i = 0; i < numAttr; ++i) {
305                     attrIds[i] = attrList.get(i).intValue();
306                     textArray[i] = getAttributeString(attrIds[i]);
307                 }
308                 getElementAttrRspNative(numAttr, attrIds, textArray);
309                 break;
310             }
311             case MESSAGE_REGISTER_NOTIFICATION:
312                 if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
313                                       " param=" + msg.arg2);
314                 processRegisterNotification(msg.arg1, msg.arg2);
315                 break;
316 
317             case MESSAGE_PLAY_INTERVAL_TIMEOUT:
318                 if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
319                 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
320                 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)getPlayPosition());
321                 break;
322 
323             case MESSAGE_VOLUME_CHANGED:
324                 if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f)
325                                                         + " ctype=" + msg.arg2);
326 
327                 if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) {
328                     if (mVolCmdInProgress == false) {
329                         Log.e(TAG, "Unsolicited response, ignored");
330                         break;
331                     }
332                     removeMessages(MESSAGE_ABS_VOL_TIMEOUT);
333                     mVolCmdInProgress = false;
334                     mAbsVolRetryTimes = 0;
335                 }
336                 if (mAbsoluteVolume != msg.arg1 && (msg.arg2 == AVRC_RSP_ACCEPT ||
337                                                     msg.arg2 == AVRC_RSP_CHANGED ||
338                                                     msg.arg2 == AVRC_RSP_INTERIM)) {
339                     byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD
340                     notifyVolumeChanged(absVol);
341                     mAbsoluteVolume = absVol;
342                     long pecentVolChanged = ((long)absVol * 100) / 0x7f;
343                     Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%");
344                 } else if (msg.arg2 == AVRC_RSP_REJ) {
345                     Log.e(TAG, "setAbsoluteVolume call rejected");
346                 }
347                 break;
348 
349             case MESSAGE_ADJUST_VOLUME:
350                 if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1);
351                 if (mVolCmdInProgress) {
352                     if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
353                     break;
354                 }
355                 // Wait on verification on volume from device, before changing the volume.
356                 if (mAbsoluteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) {
357                     int setVol = Math.min(AVRCP_MAX_VOL,
358                                  Math.max(0, mAbsoluteVolume + msg.arg1*mVolumeStep));
359                     if (setVolumeNative(setVol)) {
360                         sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
361                                            CMD_TIMEOUT_DELAY);
362                         mVolCmdInProgress = true;
363                         mLastDirection = msg.arg1;
364                         mLastSetVolume = setVol;
365                     }
366                 } else {
367                     Log.e(TAG, "Unknown direction in MESSAGE_ADJUST_VOLUME");
368                 }
369                 break;
370 
371             case MESSAGE_SET_ABSOLUTE_VOLUME:
372                 if (DEBUG) Log.v(TAG, "MESSAGE_SET_ABSOLUTE_VOLUME");
373                 if (mVolCmdInProgress) {
374                     if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
375                     break;
376                 }
377                 if (setVolumeNative(msg.arg1)) {
378                     sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
379                     mVolCmdInProgress = true;
380                     mLastSetVolume = msg.arg1;
381                 }
382                 break;
383 
384             case MESSAGE_ABS_VOL_TIMEOUT:
385                 if (DEBUG) Log.v(TAG, "MESSAGE_ABS_VOL_TIMEOUT: Volume change cmd timed out.");
386                 mVolCmdInProgress = false;
387                 if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
388                     mAbsVolRetryTimes = 0;
389                 } else {
390                     mAbsVolRetryTimes += 1;
391                     if (setVolumeNative(mLastSetVolume)) {
392                         sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
393                                            CMD_TIMEOUT_DELAY);
394                         mVolCmdInProgress = true;
395                     }
396                 }
397                 break;
398 
399             case MESSAGE_FAST_FORWARD:
400             case MESSAGE_REWIND:
401                 if(msg.what == MESSAGE_FAST_FORWARD) {
402                     if((mTransportControlFlags &
403                         RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD) != 0) {
404                     int keyState = msg.arg1 == KEY_STATE_PRESS ?
405                         KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
406                     KeyEvent keyEvent =
407                         new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
408                     mRemoteController.sendMediaKeyEvent(keyEvent);
409                     break;
410                     }
411                 } else if((mTransportControlFlags &
412                         RemoteControlClient.FLAG_KEY_MEDIA_REWIND) != 0) {
413                     int keyState = msg.arg1 == KEY_STATE_PRESS ?
414                         KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
415                     KeyEvent keyEvent =
416                         new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND);
417                     mRemoteController.sendMediaKeyEvent(keyEvent);
418                     break;
419                 }
420 
421                 int skipAmount;
422                 if (msg.what == MESSAGE_FAST_FORWARD) {
423                     if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD");
424                     removeMessages(MESSAGE_FAST_FORWARD);
425                     skipAmount = BASE_SKIP_AMOUNT;
426                 } else {
427                     if (DEBUG) Log.v(TAG, "MESSAGE_REWIND");
428                     removeMessages(MESSAGE_REWIND);
429                     skipAmount = -BASE_SKIP_AMOUNT;
430                 }
431 
432                 if (hasMessages(MESSAGE_CHANGE_PLAY_POS) &&
433                         (skipAmount != mSkipAmount)) {
434                     Log.w(TAG, "missing release button event:" + mSkipAmount);
435                 }
436 
437                 if ((!hasMessages(MESSAGE_CHANGE_PLAY_POS)) ||
438                         (skipAmount != mSkipAmount)) {
439                     mSkipStartTime = SystemClock.elapsedRealtime();
440                 }
441 
442                 removeMessages(MESSAGE_CHANGE_PLAY_POS);
443                 if (msg.arg1 == KEY_STATE_PRESS) {
444                     mSkipAmount = skipAmount;
445                     changePositionBy(mSkipAmount * getSkipMultiplier());
446                     Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
447                     posMsg.arg1 = 1;
448                     sendMessageDelayed(posMsg, SKIP_PERIOD);
449                 }
450 
451                 break;
452 
453             case MESSAGE_CHANGE_PLAY_POS:
454                 if (DEBUG) Log.v(TAG, "MESSAGE_CHANGE_PLAY_POS:" + msg.arg1);
455                 changePositionBy(mSkipAmount * getSkipMultiplier());
456                 if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) {
457                     Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
458                     posMsg.arg1 = msg.arg1 + 1;
459                     sendMessageDelayed(posMsg, SKIP_PERIOD);
460                 }
461                 break;
462 
463             case MESSAGE_SET_A2DP_AUDIO_STATE:
464                 if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1);
465                 updateA2dpAudioState(msg.arg1);
466                 break;
467             }
468         }
469     }
470 
updateA2dpAudioState(int state)471     private void updateA2dpAudioState(int state) {
472         boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
473         if (isPlaying != isPlayingState(mCurrentPlayState)) {
474             /* if a2dp is streaming, check to make sure music is active */
475             if ( (isPlaying) && !mAudioManager.isMusicActive())
476                 return;
477             updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING :
478                                  RemoteControlClient.PLAYSTATE_PAUSED,
479                                  RemoteControlClient.PLAYBACK_POSITION_INVALID);
480         }
481     }
482 
updatePlayPauseState(int state, long currentPosMs)483     private void updatePlayPauseState(int state, long currentPosMs) {
484         if (DEBUG) Log.v(TAG,
485                 "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state);
486         boolean oldPosValid = (mCurrentPosMs !=
487                                RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
488         int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
489         int newPlayStatus = convertPlayStateToPlayStatus(state);
490 
491         if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) &&
492             (mCurrentPlayState != state) && oldPosValid) {
493             mCurrentPosMs = getPlayPosition();
494         }
495 
496         if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) {
497             mCurrentPosMs = currentPosMs;
498         }
499         if ((state == RemoteControlClient.PLAYSTATE_PLAYING) &&
500             ((currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) ||
501             (mCurrentPlayState != RemoteControlClient.PLAYSTATE_PLAYING))) {
502             mPlayStartTimeMs = SystemClock.elapsedRealtime();
503         }
504         mCurrentPlayState = state;
505 
506         boolean newPosValid = (mCurrentPosMs !=
507                                RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
508         long playPosition = getPlayPosition();
509         mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
510         /* need send play position changed notification when play status is changed */
511         if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) &&
512             ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) ||
513              (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) {
514             mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
515             registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition);
516         }
517         if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid &&
518             (state == RemoteControlClient.PLAYSTATE_PLAYING)) {
519             Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
520             mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition);
521         }
522 
523         if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) {
524             mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
525             registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
526         }
527     }
528 
updateTransportControls(int transportControlFlags)529     private void updateTransportControls(int transportControlFlags) {
530         mTransportControlFlags = transportControlFlags;
531     }
532 
533     class Metadata {
534         private String artist;
535         private String trackTitle;
536         private String albumTitle;
537 
Metadata()538         public Metadata() {
539             artist = null;
540             trackTitle = null;
541             albumTitle = null;
542         }
543 
toString()544         public String toString() {
545             return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" +
546                    albumTitle + "]";
547         }
548     }
549 
updateMetadata(MetadataEditor data)550     private void updateMetadata(MetadataEditor data) {
551         String oldMetadata = mMetadata.toString();
552         mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null);
553         mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null);
554         mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null);
555         if (!oldMetadata.equals(mMetadata.toString())) {
556             mTrackNumber++;
557             if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
558                 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
559                 sendTrackChangedRsp();
560             }
561 
562             if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
563                 mCurrentPosMs = 0L;
564                 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
565                     mPlayStartTimeMs = SystemClock.elapsedRealtime();
566                 }
567             }
568             /* need send play position changed notification when track is changed */
569             if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) {
570                 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
571                 registerNotificationRspPlayPosNative(mPlayPosChangedNT,
572                                                      (int)getPlayPosition());
573                 mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
574             }
575         }
576         if (DEBUG) Log.v(TAG, "mMetadata=" + mMetadata.toString());
577 
578         mSongLengthMs = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
579                 RemoteControlClient.PLAYBACK_POSITION_INVALID);
580         if (DEBUG) Log.v(TAG, "duration=" + mSongLengthMs);
581     }
582 
getRcFeatures(byte[] address, int features)583     private void getRcFeatures(byte[] address, int features) {
584         Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0,
585                                              Utils.getAddressStringFromByte(address));
586         mHandler.sendMessage(msg);
587     }
588 
getPlayStatus()589     private void getPlayStatus() {
590         Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
591         mHandler.sendMessage(msg);
592     }
593 
getElementAttr(byte numAttr, int[] attrs)594     private void getElementAttr(byte numAttr, int[] attrs) {
595         int i;
596         ArrayList<Integer> attrList = new ArrayList<Integer>();
597         for (i = 0; i < numAttr; ++i) {
598             attrList.add(attrs[i]);
599         }
600         Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList);
601         mHandler.sendMessage(msg);
602     }
603 
registerNotification(int eventId, int param)604     private void registerNotification(int eventId, int param) {
605         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
606         mHandler.sendMessage(msg);
607     }
608 
processRegisterNotification(int eventId, int param)609     private void processRegisterNotification(int eventId, int param) {
610         switch (eventId) {
611             case EVT_PLAY_STATUS_CHANGED:
612                 mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
613                 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
614                                        convertPlayStateToPlayStatus(mCurrentPlayState));
615                 break;
616 
617             case EVT_TRACK_CHANGED:
618                 mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
619                 sendTrackChangedRsp();
620                 break;
621 
622             case EVT_PLAY_POS_CHANGED:
623                 long songPosition = getPlayPosition();
624                 mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
625                 mPlaybackIntervalMs = (long)param * 1000L;
626                 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
627                     mNextPosMs = songPosition + mPlaybackIntervalMs;
628                     mPrevPosMs = songPosition - mPlaybackIntervalMs;
629                     if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
630                         Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
631                         mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs);
632                     }
633                 }
634                 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition);
635                 break;
636 
637         }
638     }
639 
handlePassthroughCmd(int id, int keyState)640     private void handlePassthroughCmd(int id, int keyState) {
641         switch (id) {
642             case BluetoothAvrcp.PASSTHROUGH_ID_REWIND:
643                 rewind(keyState);
644                 break;
645             case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR:
646                 fastForward(keyState);
647                 break;
648         }
649     }
650 
fastForward(int keyState)651     private void fastForward(int keyState) {
652         Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0);
653         mHandler.sendMessage(msg);
654     }
655 
rewind(int keyState)656     private void rewind(int keyState) {
657         Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0);
658         mHandler.sendMessage(msg);
659     }
660 
changePositionBy(long amount)661     private void changePositionBy(long amount) {
662         long currentPosMs = getPlayPosition();
663         if (currentPosMs == -1L) return;
664         long newPosMs = Math.max(0L, currentPosMs + amount);
665         mRemoteController.seekTo(newPosMs);
666     }
667 
getSkipMultiplier()668     private int getSkipMultiplier() {
669         long currentTime = SystemClock.elapsedRealtime();
670         long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
671         return (int) Math.min(MAX_MULTIPLIER_VALUE, multi);
672     }
673 
sendTrackChangedRsp()674     private void sendTrackChangedRsp() {
675         byte[] track = new byte[TRACK_ID_SIZE];
676         /* track is stored in big endian format */
677         for (int i = 0; i < TRACK_ID_SIZE; ++i) {
678             track[i] = (byte) (mTrackNumber >> (56 - 8 * i));
679         }
680         registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
681     }
682 
getPlayPosition()683     private long getPlayPosition() {
684         long songPosition = -1L;
685         if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
686             if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
687                 songPosition = SystemClock.elapsedRealtime() -
688                                mPlayStartTimeMs + mCurrentPosMs;
689             } else {
690                 songPosition = mCurrentPosMs;
691             }
692         }
693         if (DEBUG) Log.v(TAG, "position=" + songPosition);
694         return songPosition;
695     }
696 
getAttributeString(int attrId)697     private String getAttributeString(int attrId) {
698         String attrStr = null;
699         switch (attrId) {
700             case MEDIA_ATTR_TITLE:
701                 attrStr = mMetadata.trackTitle;
702                 break;
703 
704             case MEDIA_ATTR_ARTIST:
705                 attrStr = mMetadata.artist;
706                 break;
707 
708             case MEDIA_ATTR_ALBUM:
709                 attrStr = mMetadata.albumTitle;
710                 break;
711 
712             case MEDIA_ATTR_PLAYING_TIME:
713                 if (mSongLengthMs != 0L) {
714                     attrStr = Long.toString(mSongLengthMs);
715                 }
716                 break;
717 
718         }
719         if (attrStr == null) {
720             attrStr = new String();
721         }
722         if (DEBUG) Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr);
723         return attrStr;
724     }
725 
convertPlayStateToPlayStatus(int playState)726     private int convertPlayStateToPlayStatus(int playState) {
727         int playStatus = PLAYSTATUS_ERROR;
728         switch (playState) {
729             case RemoteControlClient.PLAYSTATE_PLAYING:
730             case RemoteControlClient.PLAYSTATE_BUFFERING:
731                 playStatus = PLAYSTATUS_PLAYING;
732                 break;
733 
734             case RemoteControlClient.PLAYSTATE_STOPPED:
735             case RemoteControlClient.PLAYSTATE_NONE:
736                 playStatus = PLAYSTATUS_STOPPED;
737                 break;
738 
739             case RemoteControlClient.PLAYSTATE_PAUSED:
740                 playStatus = PLAYSTATUS_PAUSED;
741                 break;
742 
743             case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
744             case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
745                 playStatus = PLAYSTATUS_FWD_SEEK;
746                 break;
747 
748             case RemoteControlClient.PLAYSTATE_REWINDING:
749             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
750                 playStatus = PLAYSTATUS_REV_SEEK;
751                 break;
752 
753             case RemoteControlClient.PLAYSTATE_ERROR:
754                 playStatus = PLAYSTATUS_ERROR;
755                 break;
756 
757         }
758         return playStatus;
759     }
760 
isPlayingState(int playState)761     private boolean isPlayingState(int playState) {
762         boolean isPlaying = false;
763         switch (playState) {
764             case RemoteControlClient.PLAYSTATE_PLAYING:
765             case RemoteControlClient.PLAYSTATE_BUFFERING:
766                 isPlaying = true;
767                 break;
768             default:
769                 isPlaying = false;
770                 break;
771         }
772         return isPlaying;
773     }
774 
775     /**
776      * This is called from AudioService. It will return whether this device supports abs volume.
777      * NOT USED AT THE MOMENT.
778      */
isAbsoluteVolumeSupported()779     public boolean isAbsoluteVolumeSupported() {
780         return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
781     }
782 
783     /**
784      * We get this call from AudioService. This will send a message to our handler object,
785      * requesting our handler to call setVolumeNative()
786      */
adjustVolume(int direction)787     public void adjustVolume(int direction) {
788         Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
789         mHandler.sendMessage(msg);
790     }
791 
setAbsoluteVolume(int volume)792     public void setAbsoluteVolume(int volume) {
793         int avrcpVolume = convertToAvrcpVolume(volume);
794         avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
795         mHandler.removeMessages(MESSAGE_ADJUST_VOLUME);
796         Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0);
797         mHandler.sendMessage(msg);
798     }
799 
800     /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
801      * case when the volume is change locally on the carkit. This notification is not called when
802      * the volume is changed from the phone.
803      *
804      * This method will send a message to our handler to change the local stored volume and notify
805      * AudioService to update the UI
806      */
volumeChangeCallback(int volume, int ctype)807     private void volumeChangeCallback(int volume, int ctype) {
808         Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype);
809         mHandler.sendMessage(msg);
810     }
811 
notifyVolumeChanged(int volume)812     private void notifyVolumeChanged(int volume) {
813         volume = convertToAudioStreamVolume(volume);
814         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
815                       AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
816     }
817 
convertToAudioStreamVolume(int volume)818     private int convertToAudioStreamVolume(int volume) {
819         // Rescale volume to match AudioSystem's volume
820         return (int) Math.round((double) volume*mAudioStreamMax/AVRCP_MAX_VOL);
821     }
822 
convertToAvrcpVolume(int volume)823     private int convertToAvrcpVolume(int volume) {
824         return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);
825     }
826 
827     /**
828      * This is called from A2dpStateMachine to set A2dp audio state.
829      */
setA2dpAudioState(int state)830     public void setA2dpAudioState(int state) {
831         Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0);
832         mHandler.sendMessage(msg);
833     }
834 
dump(StringBuilder sb)835     public void dump(StringBuilder sb) {
836         sb.append("AVRCP:\n");
837         ProfileService.println(sb, "mMetadata: " + mMetadata);
838         ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
839         ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
840         ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
841         ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
842         ProfileService.println(sb, "mTrackNumber: " + mTrackNumber);
843         ProfileService.println(sb, "mCurrentPosMs: " + mCurrentPosMs);
844         ProfileService.println(sb, "mPlayStartTimeMs: " + mPlayStartTimeMs);
845         ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
846         ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
847         ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
848         ProfileService.println(sb, "mNextPosMs: " + mNextPosMs);
849         ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs);
850         ProfileService.println(sb, "mSkipStartTime: " + mSkipStartTime);
851         ProfileService.println(sb, "mFeatures: " + mFeatures);
852         ProfileService.println(sb, "mAbsoluteVolume: " + mAbsoluteVolume);
853         ProfileService.println(sb, "mLastSetVolume: " + mLastSetVolume);
854         ProfileService.println(sb, "mLastDirection: " + mLastDirection);
855         ProfileService.println(sb, "mVolumeStep: " + mVolumeStep);
856         ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax);
857         ProfileService.println(sb, "mVolCmdInProgress: " + mVolCmdInProgress);
858         ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes);
859         ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
860     }
861 
862     // Do not modify without updating the HAL bt_rc.h files.
863 
864     // match up with btrc_play_status_t enum of bt_rc.h
865     final static int PLAYSTATUS_STOPPED = 0;
866     final static int PLAYSTATUS_PLAYING = 1;
867     final static int PLAYSTATUS_PAUSED = 2;
868     final static int PLAYSTATUS_FWD_SEEK = 3;
869     final static int PLAYSTATUS_REV_SEEK = 4;
870     final static int PLAYSTATUS_ERROR = 255;
871 
872     // match up with btrc_media_attr_t enum of bt_rc.h
873     final static int MEDIA_ATTR_TITLE = 1;
874     final static int MEDIA_ATTR_ARTIST = 2;
875     final static int MEDIA_ATTR_ALBUM = 3;
876     final static int MEDIA_ATTR_TRACK_NUM = 4;
877     final static int MEDIA_ATTR_NUM_TRACKS = 5;
878     final static int MEDIA_ATTR_GENRE = 6;
879     final static int MEDIA_ATTR_PLAYING_TIME = 7;
880 
881     // match up with btrc_event_id_t enum of bt_rc.h
882     final static int EVT_PLAY_STATUS_CHANGED = 1;
883     final static int EVT_TRACK_CHANGED = 2;
884     final static int EVT_TRACK_REACHED_END = 3;
885     final static int EVT_TRACK_REACHED_START = 4;
886     final static int EVT_PLAY_POS_CHANGED = 5;
887     final static int EVT_BATT_STATUS_CHANGED = 6;
888     final static int EVT_SYSTEM_STATUS_CHANGED = 7;
889     final static int EVT_APP_SETTINGS_CHANGED = 8;
890 
891     // match up with btrc_notification_type_t enum of bt_rc.h
892     final static int NOTIFICATION_TYPE_INTERIM = 0;
893     final static int NOTIFICATION_TYPE_CHANGED = 1;
894 
895     // match up with BTRC_UID_SIZE of bt_rc.h
896     final static int TRACK_ID_SIZE = 8;
897 
classInitNative()898     private native static void classInitNative();
initNative()899     private native void initNative();
cleanupNative()900     private native void cleanupNative();
getPlayStatusRspNative(int playStatus, int songLen, int songPos)901     private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray)902     private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
registerNotificationRspPlayStatusNative(int type, int playStatus)903     private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
registerNotificationRspTrackChangeNative(int type, byte[] track)904     private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
registerNotificationRspPlayPosNative(int type, int playPos)905     private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
setVolumeNative(int volume)906     private native boolean setVolumeNative(int volume);
sendPassThroughCommandNative(int keyCode, int keyState)907     private native boolean sendPassThroughCommandNative(int keyCode, int keyState);
908 
909 }
910