Avrcp.java revision 7af55657d171df8336cbcce0687d1225fe0f5312
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
17package com.android.bluetooth.avrcp;
18
19import java.util.Timer;
20import java.util.TimerTask;
21
22import android.app.PendingIntent;
23import android.bluetooth.BluetoothA2dp;
24import android.bluetooth.BluetoothAvrcp;
25import android.content.Context;
26import android.content.Intent;
27import android.graphics.Bitmap;
28import android.media.AudioManager;
29import android.media.IRemoteControlDisplay;
30import android.media.MediaMetadataRetriever;
31import android.media.RemoteControlClient;
32import android.media.RemoteController;
33import android.media.RemoteController.MetadataEditor;
34import android.os.Bundle;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.Looper;
38import android.os.Message;
39import android.os.ParcelUuid;
40import android.os.PowerManager;
41import android.os.PowerManager.WakeLock;
42import android.os.RemoteException;
43import android.os.ServiceManager;
44import android.os.SystemClock;
45import android.util.Log;
46
47import com.android.bluetooth.btservice.AdapterService;
48import com.android.bluetooth.btservice.ProfileService;
49import com.android.bluetooth.Utils;
50import com.android.internal.util.IState;
51import com.android.internal.util.State;
52import com.android.internal.util.StateMachine;
53
54import java.lang.ref.WeakReference;
55import java.util.ArrayList;
56import java.util.List;
57import java.util.Set;
58
59/**
60 * support Bluetooth AVRCP profile.
61 * support metadata, play status and event notification
62 */
63public final class Avrcp {
64    private static final boolean DEBUG = true;
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                int skipAmount;
402                if (msg.what == MESSAGE_FAST_FORWARD) {
403                    if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD");
404                    skipAmount = BASE_SKIP_AMOUNT;
405                } else {
406                    if (DEBUG) Log.v(TAG, "MESSAGE_REWIND");
407                    skipAmount = -BASE_SKIP_AMOUNT;
408                }
409
410                if (hasMessages(MESSAGE_CHANGE_PLAY_POS) &&
411                        (skipAmount != mSkipAmount)) {
412                    Log.w(TAG, "missing release button event:" + mSkipAmount);
413                }
414
415                if ((!hasMessages(MESSAGE_CHANGE_PLAY_POS)) ||
416                        (skipAmount != mSkipAmount)) {
417                    mSkipStartTime = SystemClock.elapsedRealtime();
418                }
419
420                removeMessages(MESSAGE_CHANGE_PLAY_POS);
421                if (msg.arg1 == KEY_STATE_PRESS) {
422                    mSkipAmount = skipAmount;
423                    changePositionBy(mSkipAmount * getSkipMultiplier());
424                    Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
425                    posMsg.arg1 = 1;
426                    sendMessageDelayed(posMsg, SKIP_PERIOD);
427                }
428                break;
429
430            case MESSAGE_CHANGE_PLAY_POS:
431                if (DEBUG) Log.v(TAG, "MESSAGE_CHANGE_PLAY_POS:" + msg.arg1);
432                changePositionBy(mSkipAmount * getSkipMultiplier());
433                if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) {
434                    Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
435                    posMsg.arg1 = msg.arg1 + 1;
436                    sendMessageDelayed(posMsg, SKIP_PERIOD);
437                }
438                break;
439
440            case MESSAGE_SET_A2DP_AUDIO_STATE:
441                if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1);
442                updateA2dpAudioState(msg.arg1);
443                break;
444            }
445        }
446    }
447
448    private void updateA2dpAudioState(int state) {
449        boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
450        if (isPlaying != isPlayingState(mCurrentPlayState)) {
451            updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING :
452                                 RemoteControlClient.PLAYSTATE_PAUSED,
453                                 RemoteControlClient.PLAYBACK_POSITION_INVALID);
454        }
455    }
456
457    private void updatePlayPauseState(int state, long currentPosMs) {
458        if (DEBUG) Log.v(TAG,
459                "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state);
460        boolean oldPosValid = (mCurrentPosMs !=
461                               RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
462        int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
463        int newPlayStatus = convertPlayStateToPlayStatus(state);
464
465        if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) &&
466            (mCurrentPlayState != state) && oldPosValid) {
467            mCurrentPosMs = getPlayPosition();
468        }
469
470        if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) {
471            mCurrentPosMs = currentPosMs;
472        }
473        if ((state == RemoteControlClient.PLAYSTATE_PLAYING) &&
474            ((currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) ||
475            (mCurrentPlayState != RemoteControlClient.PLAYSTATE_PLAYING))) {
476            mPlayStartTimeMs = SystemClock.elapsedRealtime();
477        }
478        mCurrentPlayState = state;
479
480        boolean newPosValid = (mCurrentPosMs !=
481                               RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
482        long playPosition = getPlayPosition();
483        mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
484        /* need send play position changed notification when play status is changed */
485        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) &&
486            ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) ||
487             (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) {
488            mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
489            registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition);
490        }
491        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid &&
492            (state == RemoteControlClient.PLAYSTATE_PLAYING)) {
493            Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
494            mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition);
495        }
496
497        if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) {
498            mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
499            registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
500        }
501    }
502
503    private void updateTransportControls(int transportControlFlags) {
504        mTransportControlFlags = transportControlFlags;
505    }
506
507    class Metadata {
508        private String artist;
509        private String trackTitle;
510        private String albumTitle;
511
512        public Metadata() {
513            artist = null;
514            trackTitle = null;
515            albumTitle = null;
516        }
517
518        public String toString() {
519            return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" +
520                   albumTitle + "]";
521        }
522    }
523
524    private void updateMetadata(MetadataEditor data) {
525        String oldMetadata = mMetadata.toString();
526        mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null);
527        mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null);
528        mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null);
529        if (!oldMetadata.equals(mMetadata.toString())) {
530            mTrackNumber++;
531            if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
532                mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
533                sendTrackChangedRsp();
534            }
535
536            if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
537                mCurrentPosMs = 0L;
538                if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
539                    mPlayStartTimeMs = SystemClock.elapsedRealtime();
540                }
541            }
542            /* need send play position changed notification when track is changed */
543            if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) {
544                mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
545                registerNotificationRspPlayPosNative(mPlayPosChangedNT,
546                                                     (int)getPlayPosition());
547                mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
548            }
549        }
550        if (DEBUG) Log.v(TAG, "mMetadata=" + mMetadata.toString());
551
552        mSongLengthMs = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
553                RemoteControlClient.PLAYBACK_POSITION_INVALID);
554        if (DEBUG) Log.v(TAG, "duration=" + mSongLengthMs);
555    }
556
557    private void getRcFeatures(byte[] address, int features) {
558        Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0,
559                                             Utils.getAddressStringFromByte(address));
560        mHandler.sendMessage(msg);
561    }
562
563    private void getPlayStatus() {
564        Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
565        mHandler.sendMessage(msg);
566    }
567
568    private void getElementAttr(byte numAttr, int[] attrs) {
569        int i;
570        ArrayList<Integer> attrList = new ArrayList<Integer>();
571        for (i = 0; i < numAttr; ++i) {
572            attrList.add(attrs[i]);
573        }
574        Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList);
575        mHandler.sendMessage(msg);
576    }
577
578    private void registerNotification(int eventId, int param) {
579        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
580        mHandler.sendMessage(msg);
581    }
582
583    private void processRegisterNotification(int eventId, int param) {
584        switch (eventId) {
585            case EVT_PLAY_STATUS_CHANGED:
586                mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
587                registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
588                                       convertPlayStateToPlayStatus(mCurrentPlayState));
589                break;
590
591            case EVT_TRACK_CHANGED:
592                mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
593                sendTrackChangedRsp();
594                break;
595
596            case EVT_PLAY_POS_CHANGED:
597                long songPosition = getPlayPosition();
598                mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
599                mPlaybackIntervalMs = (long)param * 1000L;
600                if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
601                    mNextPosMs = songPosition + mPlaybackIntervalMs;
602                    mPrevPosMs = songPosition - mPlaybackIntervalMs;
603                    if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
604                        Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
605                        mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs);
606                    }
607                }
608                registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition);
609                break;
610
611        }
612    }
613
614    private void handlePassthroughCmd(int id, int keyState) {
615        switch (id) {
616            case BluetoothAvrcp.PASSTHROUGH_ID_REWIND:
617                rewind(keyState);
618                break;
619            case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR:
620                fastForward(keyState);
621                break;
622        }
623    }
624
625    private void fastForward(int keyState) {
626        Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0);
627        mHandler.sendMessage(msg);
628    }
629
630    private void rewind(int keyState) {
631        Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0);
632        mHandler.sendMessage(msg);
633    }
634
635    private void changePositionBy(long amount) {
636        long currentPosMs = getPlayPosition();
637        if (currentPosMs == -1L) return;
638        long newPosMs = Math.max(0L, currentPosMs + amount);
639        mRemoteController.seekTo(newPosMs);
640    }
641
642    private int getSkipMultiplier() {
643        long currentTime = SystemClock.elapsedRealtime();
644        long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
645        return (int) Math.min(MAX_MULTIPLIER_VALUE, multi);
646    }
647
648    private void sendTrackChangedRsp() {
649        byte[] track = new byte[TRACK_ID_SIZE];
650        /* track is stored in big endian format */
651        for (int i = 0; i < TRACK_ID_SIZE; ++i) {
652            track[i] = (byte) (mTrackNumber >> (56 - 8 * i));
653        }
654        registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
655    }
656
657    private long getPlayPosition() {
658        long songPosition = -1L;
659        if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
660            if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
661                songPosition = SystemClock.elapsedRealtime() -
662                               mPlayStartTimeMs + mCurrentPosMs;
663            } else {
664                songPosition = mCurrentPosMs;
665            }
666        }
667        if (DEBUG) Log.v(TAG, "position=" + songPosition);
668        return songPosition;
669    }
670
671    private String getAttributeString(int attrId) {
672        String attrStr = null;
673        switch (attrId) {
674            case MEDIA_ATTR_TITLE:
675                attrStr = mMetadata.trackTitle;
676                break;
677
678            case MEDIA_ATTR_ARTIST:
679                attrStr = mMetadata.artist;
680                break;
681
682            case MEDIA_ATTR_ALBUM:
683                attrStr = mMetadata.albumTitle;
684                break;
685
686            case MEDIA_ATTR_PLAYING_TIME:
687                if (mSongLengthMs != 0L) {
688                    attrStr = Long.toString(mSongLengthMs);
689                }
690                break;
691
692        }
693        if (attrStr == null) {
694            attrStr = new String();
695        }
696        if (DEBUG) Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr);
697        return attrStr;
698    }
699
700    private int convertPlayStateToPlayStatus(int playState) {
701        int playStatus = PLAYSTATUS_ERROR;
702        switch (playState) {
703            case RemoteControlClient.PLAYSTATE_PLAYING:
704            case RemoteControlClient.PLAYSTATE_BUFFERING:
705                playStatus = PLAYSTATUS_PLAYING;
706                break;
707
708            case RemoteControlClient.PLAYSTATE_STOPPED:
709            case RemoteControlClient.PLAYSTATE_NONE:
710                playStatus = PLAYSTATUS_STOPPED;
711                break;
712
713            case RemoteControlClient.PLAYSTATE_PAUSED:
714                playStatus = PLAYSTATUS_PAUSED;
715                break;
716
717            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
718            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
719                playStatus = PLAYSTATUS_FWD_SEEK;
720                break;
721
722            case RemoteControlClient.PLAYSTATE_REWINDING:
723            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
724                playStatus = PLAYSTATUS_REV_SEEK;
725                break;
726
727            case RemoteControlClient.PLAYSTATE_ERROR:
728                playStatus = PLAYSTATUS_ERROR;
729                break;
730
731        }
732        return playStatus;
733    }
734
735    private boolean isPlayingState(int playState) {
736        boolean isPlaying = false;
737        switch (playState) {
738            case RemoteControlClient.PLAYSTATE_PLAYING:
739            case RemoteControlClient.PLAYSTATE_BUFFERING:
740                isPlaying = true;
741                break;
742            default:
743                isPlaying = false;
744                break;
745        }
746        return isPlaying;
747    }
748
749    /**
750     * This is called from AudioService. It will return whether this device supports abs volume.
751     * NOT USED AT THE MOMENT.
752     */
753    public boolean isAbsoluteVolumeSupported() {
754        return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
755    }
756
757    /**
758     * We get this call from AudioService. This will send a message to our handler object,
759     * requesting our handler to call setVolumeNative()
760     */
761    public void adjustVolume(int direction) {
762        Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
763        mHandler.sendMessage(msg);
764    }
765
766    public void setAbsoluteVolume(int volume) {
767        int avrcpVolume = convertToAvrcpVolume(volume);
768        avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
769        mHandler.removeMessages(MESSAGE_ADJUST_VOLUME);
770        Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0);
771        mHandler.sendMessage(msg);
772    }
773
774    /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
775     * case when the volume is change locally on the carkit. This notification is not called when
776     * the volume is changed from the phone.
777     *
778     * This method will send a message to our handler to change the local stored volume and notify
779     * AudioService to update the UI
780     */
781    private void volumeChangeCallback(int volume, int ctype) {
782        Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype);
783        mHandler.sendMessage(msg);
784    }
785
786    private void notifyVolumeChanged(int volume) {
787        volume = convertToAudioStreamVolume(volume);
788        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
789                      AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
790    }
791
792    private int convertToAudioStreamVolume(int volume) {
793        // Rescale volume to match AudioSystem's volume
794        return (int) Math.round((double) volume*mAudioStreamMax/AVRCP_MAX_VOL);
795    }
796
797    private int convertToAvrcpVolume(int volume) {
798        return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);
799    }
800
801    /**
802     * This is called from A2dpStateMachine to set A2dp audio state.
803     */
804    public void setA2dpAudioState(int state) {
805        Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0);
806        mHandler.sendMessage(msg);
807    }
808
809    // Do not modify without updating the HAL bt_rc.h files.
810
811    // match up with btrc_play_status_t enum of bt_rc.h
812    final static int PLAYSTATUS_STOPPED = 0;
813    final static int PLAYSTATUS_PLAYING = 1;
814    final static int PLAYSTATUS_PAUSED = 2;
815    final static int PLAYSTATUS_FWD_SEEK = 3;
816    final static int PLAYSTATUS_REV_SEEK = 4;
817    final static int PLAYSTATUS_ERROR = 255;
818
819    // match up with btrc_media_attr_t enum of bt_rc.h
820    final static int MEDIA_ATTR_TITLE = 1;
821    final static int MEDIA_ATTR_ARTIST = 2;
822    final static int MEDIA_ATTR_ALBUM = 3;
823    final static int MEDIA_ATTR_TRACK_NUM = 4;
824    final static int MEDIA_ATTR_NUM_TRACKS = 5;
825    final static int MEDIA_ATTR_GENRE = 6;
826    final static int MEDIA_ATTR_PLAYING_TIME = 7;
827
828    // match up with btrc_event_id_t enum of bt_rc.h
829    final static int EVT_PLAY_STATUS_CHANGED = 1;
830    final static int EVT_TRACK_CHANGED = 2;
831    final static int EVT_TRACK_REACHED_END = 3;
832    final static int EVT_TRACK_REACHED_START = 4;
833    final static int EVT_PLAY_POS_CHANGED = 5;
834    final static int EVT_BATT_STATUS_CHANGED = 6;
835    final static int EVT_SYSTEM_STATUS_CHANGED = 7;
836    final static int EVT_APP_SETTINGS_CHANGED = 8;
837
838    // match up with btrc_notification_type_t enum of bt_rc.h
839    final static int NOTIFICATION_TYPE_INTERIM = 0;
840    final static int NOTIFICATION_TYPE_CHANGED = 1;
841
842    // match up with BTRC_UID_SIZE of bt_rc.h
843    final static int TRACK_ID_SIZE = 8;
844
845    private native static void classInitNative();
846    private native void initNative();
847    private native void cleanupNative();
848    private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
849    private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
850    private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
851    private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
852    private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
853    private native boolean setVolumeNative(int volume);
854    private native boolean sendPassThroughCommandNative(int keyCode, int keyState);
855
856}
857