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