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 Timer mTimer;
     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 
     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_FF_REW_TIMEOUT = 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 int CMD_TIMEOUT_DELAY = 2000;
    133     private static final int MAX_ERROR_RETRY_TIMES = 3;
    134     private static final int AVRCP_MAX_VOL = 127;
    135     private static final int AVRCP_BASE_VOLUME_STEP = 1;
    136 
    137     static {
    138         classInitNative();
    139     }
    140 
    141     private Avrcp(Context context) {
    142         mMetadata = new Metadata();
    143         mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
    144         mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
    145         mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
    146         mTrackNumber = -1L;
    147         mCurrentPosMs = 0L;
    148         mPlayStartTimeMs = -1L;
    149         mSongLengthMs = 0L;
    150         mPlaybackIntervalMs = 0L;
    151         mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
    152         mTimer = null;
    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                 final 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                 removeMessages(MESSAGE_FF_REW_TIMEOUT);
    422                 if (msg.arg1 == KEY_STATE_PRESS) {
    423                     if (mTimer == null) {
    424                         /** Begin fast forwarding */
    425                         mSkipStartTime = SystemClock.elapsedRealtime();
    426                         TimerTask task = new TimerTask() {
    427                             @Override
    428                             public void run() {
    429                                 changePositionBy(skipAmount*getSkipMultiplier());
    430                             }
    431                         };
    432                         mTimer = new Timer();
    433                         mTimer.schedule(task, 0, SKIP_PERIOD);
    434                     }
    435                     sendMessageDelayed(obtainMessage(MESSAGE_FF_REW_TIMEOUT), BUTTON_TIMEOUT_TIME);
    436                 } else if (msg.arg1 == KEY_STATE_RELEASE && mTimer != null) {
    437                     mTimer.cancel();
    438                     mTimer = null;
    439                 }
    440                 break;
    441 
    442             case MESSAGE_FF_REW_TIMEOUT:
    443                 if (DEBUG) Log.v(TAG, "MESSAGE_FF_REW_TIMEOUT: FF/REW response timed out");
    444                 if (mTimer != null) {
    445                     mTimer.cancel();
    446                     mTimer = null;
    447                 }
    448                 break;
    449             }
    450         }
    451     }
    452 
    453     private void updatePlayPauseState(int state, long currentPosMs) {
    454         if (DEBUG) Log.v(TAG,
    455                 "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state);
    456         boolean oldPosValid = (mCurrentPosMs !=
    457                                RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
    458         int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
    459         int newPlayStatus = convertPlayStateToPlayStatus(state);
    460 
    461         if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) &&
    462             (mCurrentPlayState != state) && oldPosValid) {
    463             mCurrentPosMs = getPlayPosition();
    464         }
    465 
    466         mCurrentPlayState = state;
    467         if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) {
    468             mCurrentPosMs = currentPosMs;
    469         }
    470         if (state == RemoteControlClient.PLAYSTATE_PLAYING) {
    471             mPlayStartTimeMs = SystemClock.elapsedRealtime();
    472         }
    473 
    474         boolean newPosValid = (mCurrentPosMs !=
    475                                RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
    476         long playPosition = getPlayPosition();
    477         mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
    478         /* need send play position changed notification when play status is changed */
    479         if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) &&
    480             ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) ||
    481              (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) {
    482             mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
    483             registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition);
    484         }
    485         if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid &&
    486             (state == RemoteControlClient.PLAYSTATE_PLAYING)) {
    487             Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
    488             mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition);
    489         }
    490 
    491         if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) {
    492             mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
    493             registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
    494         }
    495     }
    496 
    497     private void updateTransportControls(int transportControlFlags) {
    498         mTransportControlFlags = transportControlFlags;
    499     }
    500 
    501     class Metadata {
    502         private String artist;
    503         private String trackTitle;
    504         private String albumTitle;
    505 
    506         public Metadata() {
    507             artist = null;
    508             trackTitle = null;
    509             albumTitle = null;
    510         }
    511 
    512         public String toString() {
    513             return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" +
    514                    albumTitle + "]";
    515         }
    516     }
    517 
    518     private String getMdString(Bundle data, int id) {
    519         return data.getString(Integer.toString(id));
    520     }
    521 
    522     private long getMdLong(Bundle data, int id) {
    523         return data.getLong(Integer.toString(id));
    524     }
    525 
    526     private void updateMetadata(Bundle data) {
    527         String oldMetadata = mMetadata.toString();
    528         mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
    529         mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
    530         mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
    531         if (!oldMetadata.equals(mMetadata.toString())) {
    532             mTrackNumber++;
    533             if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
    534                 mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
    535                 sendTrackChangedRsp();
    536             }
    537 
    538             if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
    539                 mCurrentPosMs = 0L;
    540                 if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
    541                     mPlayStartTimeMs = SystemClock.elapsedRealtime();
    542                 }
    543             }
    544             /* need send play position changed notification when track is changed */
    545             if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) {
    546                 mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
    547                 registerNotificationRspPlayPosNative(mPlayPosChangedNT,
    548                                                      (int)getPlayPosition());
    549                 mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
    550             }
    551         }
    552         if (DEBUG) Log.v(TAG, "mMetadata=" + mMetadata.toString());
    553 
    554         mSongLengthMs = getMdLong(data, MediaMetadataRetriever.METADATA_KEY_DURATION);
    555         if (DEBUG) Log.v(TAG, "duration=" + mSongLengthMs);
    556     }
    557 
    558     private void getRcFeatures(byte[] address, int features) {
    559         Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0,
    560                                              Utils.getAddressStringFromByte(address));
    561         mHandler.sendMessage(msg);
    562     }
    563 
    564     private void getPlayStatus() {
    565         Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
    566         mHandler.sendMessage(msg);
    567     }
    568 
    569     private void getElementAttr(byte numAttr, int[] attrs) {
    570         int i;
    571         ArrayList<Integer> attrList = new ArrayList<Integer>();
    572         for (i = 0; i < numAttr; ++i) {
    573             attrList.add(attrs[i]);
    574         }
    575         Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, (int)numAttr, 0, attrList);
    576         mHandler.sendMessage(msg);
    577     }
    578 
    579     private void registerNotification(int eventId, int param) {
    580         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
    581         mHandler.sendMessage(msg);
    582     }
    583 
    584     private void processRegisterNotification(int eventId, int param) {
    585         switch (eventId) {
    586             case EVT_PLAY_STATUS_CHANGED:
    587                 mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
    588                 registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
    589                                        convertPlayStateToPlayStatus(mCurrentPlayState));
    590                 break;
    591 
    592             case EVT_TRACK_CHANGED:
    593                 mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
    594                 sendTrackChangedRsp();
    595                 break;
    596 
    597             case EVT_PLAY_POS_CHANGED:
    598                 long songPosition = getPlayPosition();
    599                 mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
    600                 mPlaybackIntervalMs = (long)param * 1000L;
    601                 if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
    602                     mNextPosMs = songPosition + mPlaybackIntervalMs;
    603                     mPrevPosMs = songPosition - mPlaybackIntervalMs;
    604                     if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
    605                         Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
    606                         mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs);
    607                     }
    608                 }
    609                 registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition);
    610                 break;
    611 
    612         }
    613     }
    614 
    615     private void handlePassthroughCmd(int id, int keyState) {
    616         switch (id) {
    617             case AVRC_ID_REWIND:
    618                 rewind(keyState);
    619                 break;
    620             case AVRC_ID_FAST_FOR:
    621                 fastForward(keyState);
    622                 break;
    623         }
    624     }
    625 
    626     private void fastForward(int keyState) {
    627         Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0);
    628         mHandler.sendMessage(msg);
    629     }
    630 
    631     private void rewind(int keyState) {
    632         Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0);
    633         mHandler.sendMessage(msg);
    634     }
    635 
    636     private void changePositionBy(long amount) {
    637         long currentPosMs = getPlayPosition();
    638         if (currentPosMs == -1L) return;
    639         long newPosMs = Math.max(0L, currentPosMs + amount);
    640         mAudioManager.setRemoteControlClientPlaybackPosition(mClientGeneration,
    641                 newPosMs);
    642     }
    643 
    644     private int getSkipMultiplier() {
    645         long currentTime = SystemClock.elapsedRealtime();
    646         return (int) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
    647     }
    648 
    649     private void sendTrackChangedRsp() {
    650         byte[] track = new byte[TRACK_ID_SIZE];
    651         /* track is stored in big endian format */
    652         for (int i = 0; i < TRACK_ID_SIZE; ++i) {
    653             track[i] = (byte) (mTrackNumber >> (56 - 8 * i));
    654         }
    655         registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
    656     }
    657 
    658     private long getPlayPosition() {
    659         long songPosition = -1L;
    660         if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
    661             if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
    662                 songPosition = SystemClock.elapsedRealtime() -
    663                                mPlayStartTimeMs + mCurrentPosMs;
    664             } else {
    665                 songPosition = mCurrentPosMs;
    666             }
    667         }
    668         if (DEBUG) Log.v(TAG, "position=" + songPosition);
    669         return songPosition;
    670     }
    671 
    672     private String getAttributeString(int attrId) {
    673         String attrStr = null;
    674         switch (attrId) {
    675             case MEDIA_ATTR_TITLE:
    676                 attrStr = mMetadata.trackTitle;
    677                 break;
    678 
    679             case MEDIA_ATTR_ARTIST:
    680                 attrStr = mMetadata.artist;
    681                 break;
    682 
    683             case MEDIA_ATTR_ALBUM:
    684                 attrStr = mMetadata.albumTitle;
    685                 break;
    686 
    687             case MEDIA_ATTR_PLAYING_TIME:
    688                 if (mSongLengthMs != 0L) {
    689                     attrStr = Long.toString(mSongLengthMs);
    690                 }
    691                 break;
    692 
    693         }
    694         if (attrStr == null) {
    695             attrStr = new String();
    696         }
    697         if (DEBUG) Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr);
    698         return attrStr;
    699     }
    700 
    701     private int convertPlayStateToPlayStatus(int playState) {
    702         int playStatus = PLAYSTATUS_ERROR;
    703         switch (playState) {
    704             case RemoteControlClient.PLAYSTATE_PLAYING:
    705             case RemoteControlClient.PLAYSTATE_BUFFERING:
    706                 playStatus = PLAYSTATUS_PLAYING;
    707                 break;
    708 
    709             case RemoteControlClient.PLAYSTATE_STOPPED:
    710             case RemoteControlClient.PLAYSTATE_NONE:
    711                 playStatus = PLAYSTATUS_STOPPED;
    712                 break;
    713 
    714             case RemoteControlClient.PLAYSTATE_PAUSED:
    715                 playStatus = PLAYSTATUS_PAUSED;
    716                 break;
    717 
    718             case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
    719             case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
    720                 playStatus = PLAYSTATUS_FWD_SEEK;
    721                 break;
    722 
    723             case RemoteControlClient.PLAYSTATE_REWINDING:
    724             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
    725                 playStatus = PLAYSTATUS_REV_SEEK;
    726                 break;
    727 
    728             case RemoteControlClient.PLAYSTATE_ERROR:
    729                 playStatus = PLAYSTATUS_ERROR;
    730                 break;
    731 
    732         }
    733         return playStatus;
    734     }
    735 
    736     /**
    737      * This is called from AudioService. It will return whether this device supports abs volume.
    738      * NOT USED AT THE MOMENT.
    739      */
    740     public boolean isAbsoluteVolumeSupported() {
    741         return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
    742     }
    743 
    744     /**
    745      * We get this call from AudioService. This will send a message to our handler object,
    746      * requesting our handler to call setVolumeNative()
    747      */
    748     public void adjustVolume(int direction) {
    749         Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
    750         mHandler.sendMessage(msg);
    751     }
    752 
    753     public void setAbsoluteVolume(int volume) {
    754         int avrcpVolume = convertToAvrcpVolume(volume);
    755         avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
    756         mHandler.removeMessages(MESSAGE_ADJUST_VOLUME);
    757         Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0);
    758         mHandler.sendMessage(msg);
    759 
    760     }
    761 
    762     /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
    763      * case when the volume is change locally on the carkit. This notification is not called when
    764      * the volume is changed from the phone.
    765      *
    766      * This method will send a message to our handler to change the local stored volume and notify
    767      * AudioService to update the UI
    768      */
    769     private void volumeChangeCallback(int volume, int ctype) {
    770         Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype);
    771         mHandler.sendMessage(msg);
    772     }
    773 
    774     private void notifyVolumeChanged(int volume) {
    775         volume = convertToAudioStreamVolume(volume);
    776         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
    777                       AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
    778     }
    779 
    780     private int convertToAudioStreamVolume(int volume) {
    781         // Rescale volume to match AudioSystem's volume
    782         return (int) Math.ceil((double) volume*mAudioStreamMax/AVRCP_MAX_VOL);
    783     }
    784 
    785     private int convertToAvrcpVolume(int volume) {
    786         return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);
    787     }
    788 
    789     // Do not modify without updating the HAL bt_rc.h files.
    790 
    791     // match up with btrc_play_status_t enum of bt_rc.h
    792     final static int PLAYSTATUS_STOPPED = 0;
    793     final static int PLAYSTATUS_PLAYING = 1;
    794     final static int PLAYSTATUS_PAUSED = 2;
    795     final static int PLAYSTATUS_FWD_SEEK = 3;
    796     final static int PLAYSTATUS_REV_SEEK = 4;
    797     final static int PLAYSTATUS_ERROR = 255;
    798 
    799     // match up with btrc_media_attr_t enum of bt_rc.h
    800     final static int MEDIA_ATTR_TITLE = 1;
    801     final static int MEDIA_ATTR_ARTIST = 2;
    802     final static int MEDIA_ATTR_ALBUM = 3;
    803     final static int MEDIA_ATTR_TRACK_NUM = 4;
    804     final static int MEDIA_ATTR_NUM_TRACKS = 5;
    805     final static int MEDIA_ATTR_GENRE = 6;
    806     final static int MEDIA_ATTR_PLAYING_TIME = 7;
    807 
    808     // match up with btrc_event_id_t enum of bt_rc.h
    809     final static int EVT_PLAY_STATUS_CHANGED = 1;
    810     final static int EVT_TRACK_CHANGED = 2;
    811     final static int EVT_TRACK_REACHED_END = 3;
    812     final static int EVT_TRACK_REACHED_START = 4;
    813     final static int EVT_PLAY_POS_CHANGED = 5;
    814     final static int EVT_BATT_STATUS_CHANGED = 6;
    815     final static int EVT_SYSTEM_STATUS_CHANGED = 7;
    816     final static int EVT_APP_SETTINGS_CHANGED = 8;
    817 
    818     // match up with btrc_notification_type_t enum of bt_rc.h
    819     final static int NOTIFICATION_TYPE_INTERIM = 0;
    820     final static int NOTIFICATION_TYPE_CHANGED = 1;
    821 
    822     // match up with BTRC_UID_SIZE of bt_rc.h
    823     final static int TRACK_ID_SIZE = 8;
    824 
    825     private native static void classInitNative();
    826     private native void initNative();
    827     private native void cleanupNative();
    828     private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
    829     private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
    830     private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
    831     private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
    832     private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
    833     private native boolean setVolumeNative(int volume);
    834 }
    835