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