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