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