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