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