Avrcp.java revision 22bad7047263a6924423d12718738fdcc7b0352c
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            /* 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    // Do not modify without updating the HAL bt_rc.h files.
836
837    // match up with btrc_play_status_t enum of bt_rc.h
838    final static int PLAYSTATUS_STOPPED = 0;
839    final static int PLAYSTATUS_PLAYING = 1;
840    final static int PLAYSTATUS_PAUSED = 2;
841    final static int PLAYSTATUS_FWD_SEEK = 3;
842    final static int PLAYSTATUS_REV_SEEK = 4;
843    final static int PLAYSTATUS_ERROR = 255;
844
845    // match up with btrc_media_attr_t enum of bt_rc.h
846    final static int MEDIA_ATTR_TITLE = 1;
847    final static int MEDIA_ATTR_ARTIST = 2;
848    final static int MEDIA_ATTR_ALBUM = 3;
849    final static int MEDIA_ATTR_TRACK_NUM = 4;
850    final static int MEDIA_ATTR_NUM_TRACKS = 5;
851    final static int MEDIA_ATTR_GENRE = 6;
852    final static int MEDIA_ATTR_PLAYING_TIME = 7;
853
854    // match up with btrc_event_id_t enum of bt_rc.h
855    final static int EVT_PLAY_STATUS_CHANGED = 1;
856    final static int EVT_TRACK_CHANGED = 2;
857    final static int EVT_TRACK_REACHED_END = 3;
858    final static int EVT_TRACK_REACHED_START = 4;
859    final static int EVT_PLAY_POS_CHANGED = 5;
860    final static int EVT_BATT_STATUS_CHANGED = 6;
861    final static int EVT_SYSTEM_STATUS_CHANGED = 7;
862    final static int EVT_APP_SETTINGS_CHANGED = 8;
863
864    // match up with btrc_notification_type_t enum of bt_rc.h
865    final static int NOTIFICATION_TYPE_INTERIM = 0;
866    final static int NOTIFICATION_TYPE_CHANGED = 1;
867
868    // match up with BTRC_UID_SIZE of bt_rc.h
869    final static int TRACK_ID_SIZE = 8;
870
871    private native static void classInitNative();
872    private native void initNative();
873    private native void cleanupNative();
874    private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
875    private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
876    private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
877    private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
878    private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
879    private native boolean setVolumeNative(int volume);
880    private native boolean sendPassThroughCommandNative(int keyCode, int keyState);
881
882}
883