Avrcp.java revision 17675906064bb72fdcca75baa56cdf8bb8968d01
1c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu/*
2c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * Copyright (C) 2012 The Android Open Source Project
3c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu *
4c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * Licensed under the Apache License, Version 2.0 (the "License");
5c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * you may not use this file except in compliance with the License.
6c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * You may obtain a copy of the License at
7c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu *
8c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu *      http://www.apache.org/licenses/LICENSE-2.0
9c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu *
10c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * Unless required by applicable law or agreed to in writing, software
11c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * distributed under the License is distributed on an "AS IS" BASIS,
12c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * See the License for the specific language governing permissions and
14c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * limitations under the License.
15c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu */
16c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
17c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xupackage com.android.bluetooth.a2dp;
18c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
19ace834feb02adabd61f628c4471147aea02d939cJohn Duimport java.util.Timer;
20ace834feb02adabd61f628c4471147aea02d939cJohn Duimport java.util.TimerTask;
21ace834feb02adabd61f628c4471147aea02d939cJohn Du
22c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.app.PendingIntent;
23c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.content.Context;
24aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xuimport android.content.Intent;
25c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.graphics.Bitmap;
26c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.media.AudioManager;
27c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.media.IRemoteControlDisplay;
28c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.media.MediaMetadataRetriever;
29c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.media.RemoteControlClient;
30c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Bundle;
31c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Handler;
32c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.HandlerThread;
33c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Looper;
34c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Message;
35c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.ParcelUuid;
36c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.PowerManager;
37c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.PowerManager.WakeLock;
38c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.RemoteException;
39c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.ServiceManager;
40aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xuimport android.os.SystemClock;
41c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.util.Log;
42c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.bluetooth.btservice.AdapterService;
43c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.bluetooth.btservice.ProfileService;
44aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xuimport com.android.bluetooth.Utils;
45c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.internal.util.IState;
46c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.internal.util.State;
47c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.internal.util.StateMachine;
48c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.lang.ref.WeakReference;
49c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.util.ArrayList;
50c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.util.List;
51c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.util.Set;
52c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
53c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu/**
54c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * support Bluetooth AVRCP profile.
55c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * support metadata, play status and event notification
56c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu */
57c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xufinal class Avrcp {
5817675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final boolean DEBUG = true;
59c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static final String TAG = "Avrcp";
60c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
61c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private Context mContext;
62c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private final AudioManager mAudioManager;
63c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private AvrcpMessageHandler mHandler;
64c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private IRemoteControlDisplayWeak mRemoteControlDisplay;
65c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mClientGeneration;
66c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private Metadata mMetadata;
67c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mTransportControlFlags;
68c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mCurrentPlayState;
69c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mPlayStatusChangedNT;
70c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mTrackChangedNT;
71c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private long mTrackNumber;
72aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mCurrentPosMs;
73aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mPlayStartTimeMs;
74aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mSongLengthMs;
75aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mPlaybackIntervalMs;
76aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private int mPlayPosChangedNT;
77aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mNextPosMs;
78aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mPrevPosMs;
79ace834feb02adabd61f628c4471147aea02d939cJohn Du    private long mSkipStartTime;
80ace834feb02adabd61f628c4471147aea02d939cJohn Du    private Timer mTimer;
8117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mFeatures;
8217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mAbsoluteVolume;
8317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mLastSetVolume;
8417675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mLastDirection;
8517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mVolumeStep;
8617675906064bb72fdcca75baa56cdf8bb8968d01John Du    private boolean mVolCmdInProgress;
8717675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mAbsVolRetryTimes;
88ace834feb02adabd61f628c4471147aea02d939cJohn Du
89ace834feb02adabd61f628c4471147aea02d939cJohn Du    /* AVRC IDs from avrc_defs.h */
90ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int AVRC_ID_REWIND = 0x48;
91ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int AVRC_ID_FAST_FOR = 0x49;
92aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
9317675906064bb72fdcca75baa56cdf8bb8968d01John Du    /* BTRC features */
9417675906064bb72fdcca75baa56cdf8bb8968d01John Du    public static final int BTRC_FEAT_METADATA = 0x01;
9517675906064bb72fdcca75baa56cdf8bb8968d01John Du    public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
9617675906064bb72fdcca75baa56cdf8bb8968d01John Du    public static final int BTRC_FEAT_BROWSE = 0x04;
9717675906064bb72fdcca75baa56cdf8bb8968d01John Du
9817675906064bb72fdcca75baa56cdf8bb8968d01John Du    /* AVRC response codes, from avrc_defs */
9917675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_NOT_IMPL = 8;
10017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_ACCEPT = 9;
10117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_REJ = 10;
10217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_IN_TRANS = 11;
10317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_IMPL_STBL = 12;
10417675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_CHANGED = 13;
10517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_INTERIM = 15;
10617675906064bb72fdcca75baa56cdf8bb8968d01John Du
10717675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_GET_RC_FEATURES = 1;
10817675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_GET_PLAY_STATUS = 2;
10917675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_GET_ELEM_ATTRS = 3;
11017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_REGISTER_NOTIFICATION = 4;
11117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5;
11217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_VOLUME_CHANGED = 6;
11317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_ADJUST_VOLUME = 7;
11417675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8;
11517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_ABS_VOL_TIMEOUT = 9;
11617675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_FAST_FORWARD = 10;
11717675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_REWIND = 11;
11817675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_FF_REW_TIMEOUT = 12;
119c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static final int MSG_UPDATE_STATE = 100;
120c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static final int MSG_SET_METADATA = 101;
121c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
122c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static final int MSG_SET_ARTWORK = 103;
123c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static final int MSG_SET_GENERATION_ID = 104;
124c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
125ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int BUTTON_TIMEOUT_TIME = 2000;
126ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int BASE_SKIP_AMOUNT = 2000;
127ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int KEY_STATE_PRESS = 1;
128ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int KEY_STATE_RELEASE = 0;
129ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int SKIP_PERIOD = 400;
130ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int SKIP_DOUBLE_INTERVAL = 3000;
13117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int CMD_TIMEOUT_DELAY = 2000;
13217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MAX_ERROR_RETRY_TIMES = 3;
13317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AUDIO_STREAM_MAX_VOL = 150;
13417675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRCP_MAX_VOL = 127;
13517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRCP_BASE_VOLUME_STEP = 1;
136ace834feb02adabd61f628c4471147aea02d939cJohn Du
137c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    static {
138c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        classInitNative();
139c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
140c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
141c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private Avrcp(Context context) {
142c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mMetadata = new Metadata();
143c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
144c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
145c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
14679d176b0f3d6cb33c7e52be6641fd4808ba87e93Zhihai Xu        mTrackNumber = -1L;
147f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        mCurrentPosMs = 0L;
148aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mPlayStartTimeMs = -1L;
149aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mSongLengthMs = 0L;
150aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mPlaybackIntervalMs = 0L;
151aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
152ace834feb02adabd61f628c4471147aea02d939cJohn Du        mTimer = null;
15317675906064bb72fdcca75baa56cdf8bb8968d01John Du        mFeatures = 0;
15417675906064bb72fdcca75baa56cdf8bb8968d01John Du        mAbsoluteVolume = -1;
15517675906064bb72fdcca75baa56cdf8bb8968d01John Du        mLastSetVolume = -1;
15617675906064bb72fdcca75baa56cdf8bb8968d01John Du        mLastDirection = 0;
15717675906064bb72fdcca75baa56cdf8bb8968d01John Du        mVolumeStep = AVRCP_BASE_VOLUME_STEP;
15817675906064bb72fdcca75baa56cdf8bb8968d01John Du        mVolCmdInProgress = false;
15917675906064bb72fdcca75baa56cdf8bb8968d01John Du        mAbsVolRetryTimes = 0;
160c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
161c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mContext = context;
162c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
163c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        initNative();
164c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
165c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
166c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
167c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
168c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void start() {
169c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
170c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        thread.start();
171c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Looper looper = thread.getLooper();
172c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler = new AvrcpMessageHandler(looper);
173c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mRemoteControlDisplay = new IRemoteControlDisplayWeak(mHandler);
174c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
175db367aa803836ca5b80da6b07ea2ef8a4b0ca1d2Zhihai Xu        mAudioManager.remoteControlDisplayWantsPlaybackPositionSync(
176db367aa803836ca5b80da6b07ea2ef8a4b0ca1d2Zhihai Xu                      mRemoteControlDisplay, true);
177c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
178c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
179c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    static Avrcp make(Context context) {
180c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (DEBUG) Log.v(TAG, "make");
181c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Avrcp ar = new Avrcp(context);
182c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        ar.start();
183c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        return ar;
184c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
185c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
186c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    public void doQuit() {
187c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.removeCallbacksAndMessages(null);
188c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Looper looper = mHandler.getLooper();
189c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (looper != null) {
190c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            looper.quit();
191c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
192c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mAudioManager.unregisterRemoteControlDisplay(mRemoteControlDisplay);
193c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
194c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
195c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    public void cleanup() {
196c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        cleanupNative();
197c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
198c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
199c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub {
200c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        private WeakReference<Handler> mLocalHandler;
201c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        IRemoteControlDisplayWeak(Handler handler) {
202c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            mLocalHandler = new WeakReference<Handler>(handler);
203c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
204c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
205bc10e7d58aa55da25c18d8056a0254a2b736146aZhihai Xu        @Override
206eb8907e19ad3fa8208d7a65c8aae1bf758838705Jean-Michel Trivi        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
207eb8907e19ad3fa8208d7a65c8aae1bf758838705Jean-Michel Trivi                long currentPosMs, float speed) {
208c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            Handler handler = mLocalHandler.get();
209c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            if (handler != null) {
210aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                handler.obtainMessage(MSG_UPDATE_STATE, generationId, state,
211aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                                      new Long(currentPosMs)).sendToTarget();
212c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
213c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
214c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
215c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
216c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public void setMetadata(int generationId, Bundle metadata) {
217c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            Handler handler = mLocalHandler.get();
218c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            if (handler != null) {
219c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
220c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
221c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
222c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
223c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
22493627064c68acfaef2da5deb2c9c9e73c7aa13afJean-Michel Trivi        public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
225c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            Handler handler = mLocalHandler.get();
226c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            if (handler != null) {
227c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags)
228c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                        .sendToTarget();
229c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
230c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
231c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
232c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
233c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public void setArtwork(int generationId, Bitmap bitmap) {
234c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
235c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
236c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
237c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
238c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            Handler handler = mLocalHandler.get();
239c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            if (handler != null) {
240c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget();
241c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget();
242c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
243c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
244c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
245c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
246c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
247c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                boolean clearing) throws RemoteException {
248c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            Handler handler = mLocalHandler.get();
249c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            if (handler != null) {
250c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                handler.obtainMessage(MSG_SET_GENERATION_ID,
251c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                    clientGeneration, (clearing ? 1 : 0), mediaIntent).sendToTarget();
252c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
253c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
254c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
255c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
256c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    /** Handles Avrcp messages. */
257c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private final class AvrcpMessageHandler extends Handler {
258c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        private AvrcpMessageHandler(Looper looper) {
259c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            super(looper);
260c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
261c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
262c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
263c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public void handleMessage(Message msg) {
264c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            switch (msg.what) {
265c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MSG_UPDATE_STATE:
266aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                if (mClientGeneration == msg.arg1) {
267aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                    updatePlayPauseState(msg.arg2, ((Long)msg.obj).longValue());
268aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                }
269c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
270c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
271c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MSG_SET_METADATA:
272c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj);
273c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
274c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
275c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MSG_SET_TRANSPORT_CONTROLS:
276c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2);
277c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
278c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
279c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MSG_SET_ARTWORK:
280c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (mClientGeneration == msg.arg1) {
281c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                }
282c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
283c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
284c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MSG_SET_GENERATION_ID:
285c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
286c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                mClientGeneration = msg.arg1;
287c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
288c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
28917675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_GET_RC_FEATURES:
29017675906064bb72fdcca75baa56cdf8bb8968d01John Du                String address = (String) msg.obj;
29117675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+
29217675906064bb72fdcca75baa56cdf8bb8968d01John Du                                                             ", features="+msg.arg1);
29317675906064bb72fdcca75baa56cdf8bb8968d01John Du                mFeatures = msg.arg1;
29417675906064bb72fdcca75baa56cdf8bb8968d01John Du                mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported());
29517675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
29617675906064bb72fdcca75baa56cdf8bb8968d01John Du
297c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MESSAGE_GET_PLAY_STATUS:
298c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
299aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState),
300aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                                       (int)mSongLengthMs, (int)getPlayPosition());
301c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
302c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
303c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MESSAGE_GET_ELEM_ATTRS:
304c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            {
305c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                String[] textArray;
306c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                int[] attrIds;
307c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                byte numAttr = (byte) msg.arg1;
308c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj;
309c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
310c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                attrIds = new int[numAttr];
311c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                textArray = new String[numAttr];
312c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                for (int i = 0; i < numAttr; ++i) {
313c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                    attrIds[i] = attrList.get(i).intValue();
314c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                    textArray[i] = getAttributeString(attrIds[i]);
315c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                }
316c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                getElementAttrRspNative(numAttr, attrIds, textArray);
317c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
318c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
319c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MESSAGE_REGISTER_NOTIFICATION:
320c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
321c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                                      " param=" + msg.arg2);
322c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                processRegisterNotification(msg.arg1, msg.arg2);
323c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
324c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
325aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            case MESSAGE_PLAY_INTERVAL_TIMEOUT:
326aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
327aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
328aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)getPlayPosition());
329aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                break;
330aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
33117675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_VOLUME_CHANGED:
33217675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + msg.arg1 +
33317675906064bb72fdcca75baa56cdf8bb8968d01John Du                                                              " ctype=" + msg.arg2);
33417675906064bb72fdcca75baa56cdf8bb8968d01John Du
33517675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) {
33617675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (mVolCmdInProgress == false) {
33717675906064bb72fdcca75baa56cdf8bb8968d01John Du                        Log.e(TAG, "Unsolicited response, ignored");
33817675906064bb72fdcca75baa56cdf8bb8968d01John Du                        break;
33917675906064bb72fdcca75baa56cdf8bb8968d01John Du                    }
34017675906064bb72fdcca75baa56cdf8bb8968d01John Du                    removeMessages(MESSAGE_ABS_VOL_TIMEOUT);
34117675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mVolCmdInProgress = false;
34217675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mAbsVolRetryTimes = 0;
34317675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
34417675906064bb72fdcca75baa56cdf8bb8968d01John Du                // to deal with granularity in the TG
34517675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (msg.arg2 == AVRC_RSP_ACCEPT && mAbsoluteVolume == msg.arg1) {
34617675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if ((mAbsoluteVolume == AVRCP_MAX_VOL && mLastDirection == -1)
34717675906064bb72fdcca75baa56cdf8bb8968d01John Du                     || (mAbsoluteVolume == 0 && mLastDirection == 1)
34817675906064bb72fdcca75baa56cdf8bb8968d01John Du                     || (mAbsoluteVolume != AVRCP_MAX_VOL && mAbsoluteVolume != 0)) {
34917675906064bb72fdcca75baa56cdf8bb8968d01John Du                        mVolumeStep += 1;
35017675906064bb72fdcca75baa56cdf8bb8968d01John Du                        adjustVolumeImmediate(mLastDirection);
35117675906064bb72fdcca75baa56cdf8bb8968d01John Du                    }
35217675906064bb72fdcca75baa56cdf8bb8968d01John Du                } else if (mAbsoluteVolume != msg.arg1 && (msg.arg2 == AVRC_RSP_ACCEPT ||
35317675906064bb72fdcca75baa56cdf8bb8968d01John Du                                                           msg.arg2 == AVRC_RSP_CHANGED ||
35417675906064bb72fdcca75baa56cdf8bb8968d01John Du                                                           msg.arg2 == AVRC_RSP_INTERIM)) {
35517675906064bb72fdcca75baa56cdf8bb8968d01John Du                    notifyVolumeChanged(mAbsoluteVolume, msg.arg1);
35617675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mAbsoluteVolume = msg.arg1;
35717675906064bb72fdcca75baa56cdf8bb8968d01John Du                } else if (msg.arg2 == AVRC_RSP_REJ) {
35817675906064bb72fdcca75baa56cdf8bb8968d01John Du                    Log.e(TAG, "setAbsoluteVolume call rejected");
35917675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
36017675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
36117675906064bb72fdcca75baa56cdf8bb8968d01John Du
36217675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_ADJUST_VOLUME:
36317675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1);
36417675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (mVolCmdInProgress) {
36517675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
36617675906064bb72fdcca75baa56cdf8bb8968d01John Du                    break;
36717675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
36817675906064bb72fdcca75baa56cdf8bb8968d01John Du                // Wait on verification on volume from device, before changing the volume.
36917675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (mAbsoluteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) {
37017675906064bb72fdcca75baa56cdf8bb8968d01John Du                    int setVol = Math.min(AVRCP_MAX_VOL,
37117675906064bb72fdcca75baa56cdf8bb8968d01John Du                                 Math.max(0, mAbsoluteVolume + msg.arg1*mVolumeStep));
37217675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (setVolumeNative(setVol)) {
37317675906064bb72fdcca75baa56cdf8bb8968d01John Du                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
37417675906064bb72fdcca75baa56cdf8bb8968d01John Du                                           CMD_TIMEOUT_DELAY);
37517675906064bb72fdcca75baa56cdf8bb8968d01John Du                        mVolCmdInProgress = true;
37617675906064bb72fdcca75baa56cdf8bb8968d01John Du                        mLastDirection = msg.arg1;
37717675906064bb72fdcca75baa56cdf8bb8968d01John Du                        mLastSetVolume = setVol;
37817675906064bb72fdcca75baa56cdf8bb8968d01John Du                    }
37917675906064bb72fdcca75baa56cdf8bb8968d01John Du                } else {
38017675906064bb72fdcca75baa56cdf8bb8968d01John Du                    Log.e(TAG, "Unknown direction in MESSAGE_ADJUST_VOLUME");
38117675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
38217675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
38317675906064bb72fdcca75baa56cdf8bb8968d01John Du
38417675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_SET_ABSOLUTE_VOLUME:
38517675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.v(TAG, "MESSAGE_SET_ABSOLUTE_VOLUME");
38617675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (mVolCmdInProgress) {
38717675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
38817675906064bb72fdcca75baa56cdf8bb8968d01John Du                    break;
38917675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
39017675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (setVolumeNative(msg.arg1)) {
39117675906064bb72fdcca75baa56cdf8bb8968d01John Du                    sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
39217675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mVolCmdInProgress = true;
39317675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mLastSetVolume = msg.arg1;
39417675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
39517675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
39617675906064bb72fdcca75baa56cdf8bb8968d01John Du
39717675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_ABS_VOL_TIMEOUT:
39817675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.v(TAG, "MESSAGE_ABS_VOL_TIMEOUT: Volume change cmd timed out.");
39917675906064bb72fdcca75baa56cdf8bb8968d01John Du                mVolCmdInProgress = false;
40017675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
40117675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mAbsVolRetryTimes = 0;
40217675906064bb72fdcca75baa56cdf8bb8968d01John Du                } else {
40317675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mAbsVolRetryTimes += 1;
40417675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (setVolumeNative(mLastSetVolume)) {
40517675906064bb72fdcca75baa56cdf8bb8968d01John Du                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
40617675906064bb72fdcca75baa56cdf8bb8968d01John Du                                           CMD_TIMEOUT_DELAY);
40717675906064bb72fdcca75baa56cdf8bb8968d01John Du                        mVolCmdInProgress = true;
40817675906064bb72fdcca75baa56cdf8bb8968d01John Du                    }
40917675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
41017675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
41117675906064bb72fdcca75baa56cdf8bb8968d01John Du
412ace834feb02adabd61f628c4471147aea02d939cJohn Du            case MESSAGE_FAST_FORWARD:
413ace834feb02adabd61f628c4471147aea02d939cJohn Du            case MESSAGE_REWIND:
414ace834feb02adabd61f628c4471147aea02d939cJohn Du                final int skipAmount;
415ace834feb02adabd61f628c4471147aea02d939cJohn Du                if (msg.what == MESSAGE_FAST_FORWARD) {
416ace834feb02adabd61f628c4471147aea02d939cJohn Du                    if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD");
417ace834feb02adabd61f628c4471147aea02d939cJohn Du                    skipAmount = BASE_SKIP_AMOUNT;
418ace834feb02adabd61f628c4471147aea02d939cJohn Du                } else {
419ace834feb02adabd61f628c4471147aea02d939cJohn Du                    if (DEBUG) Log.v(TAG, "MESSAGE_REWIND");
420ace834feb02adabd61f628c4471147aea02d939cJohn Du                    skipAmount = -BASE_SKIP_AMOUNT;
421ace834feb02adabd61f628c4471147aea02d939cJohn Du                }
422ace834feb02adabd61f628c4471147aea02d939cJohn Du
423ace834feb02adabd61f628c4471147aea02d939cJohn Du                removeMessages(MESSAGE_FF_REW_TIMEOUT);
424ace834feb02adabd61f628c4471147aea02d939cJohn Du                if (msg.arg1 == KEY_STATE_PRESS) {
425ace834feb02adabd61f628c4471147aea02d939cJohn Du                    if (mTimer == null) {
426ace834feb02adabd61f628c4471147aea02d939cJohn Du                        /** Begin fast forwarding */
427ace834feb02adabd61f628c4471147aea02d939cJohn Du                        mSkipStartTime = SystemClock.elapsedRealtime();
428ace834feb02adabd61f628c4471147aea02d939cJohn Du                        TimerTask task = new TimerTask() {
429ace834feb02adabd61f628c4471147aea02d939cJohn Du                            @Override
430ace834feb02adabd61f628c4471147aea02d939cJohn Du                            public void run() {
431ace834feb02adabd61f628c4471147aea02d939cJohn Du                                changePositionBy(skipAmount*getSkipMultiplier());
432ace834feb02adabd61f628c4471147aea02d939cJohn Du                            }
433ace834feb02adabd61f628c4471147aea02d939cJohn Du                        };
434ace834feb02adabd61f628c4471147aea02d939cJohn Du                        mTimer = new Timer();
435ace834feb02adabd61f628c4471147aea02d939cJohn Du                        mTimer.schedule(task, 0, SKIP_PERIOD);
436ace834feb02adabd61f628c4471147aea02d939cJohn Du                    }
437ace834feb02adabd61f628c4471147aea02d939cJohn Du                    sendMessageDelayed(obtainMessage(MESSAGE_FF_REW_TIMEOUT), BUTTON_TIMEOUT_TIME);
438ace834feb02adabd61f628c4471147aea02d939cJohn Du                } else if (msg.arg1 == KEY_STATE_RELEASE && mTimer != null) {
439ace834feb02adabd61f628c4471147aea02d939cJohn Du                    mTimer.cancel();
440ace834feb02adabd61f628c4471147aea02d939cJohn Du                    mTimer = null;
441ace834feb02adabd61f628c4471147aea02d939cJohn Du                }
442ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
443ace834feb02adabd61f628c4471147aea02d939cJohn Du
444ace834feb02adabd61f628c4471147aea02d939cJohn Du            case MESSAGE_FF_REW_TIMEOUT:
445ace834feb02adabd61f628c4471147aea02d939cJohn Du                if (DEBUG) Log.v(TAG, "MESSAGE_FF_REW_TIMEOUT: FF/REW response timed out");
446ace834feb02adabd61f628c4471147aea02d939cJohn Du                if (mTimer != null) {
447ace834feb02adabd61f628c4471147aea02d939cJohn Du                    mTimer.cancel();
448ace834feb02adabd61f628c4471147aea02d939cJohn Du                    mTimer = null;
449ace834feb02adabd61f628c4471147aea02d939cJohn Du                }
450ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
451c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
452c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
453c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
454c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
455aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private void updatePlayPauseState(int state, long currentPosMs) {
456c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (DEBUG) Log.v(TAG,
457aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state);
458f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        boolean oldPosValid = (mCurrentPosMs !=
459f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu                               RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
460c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
461c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        int newPlayStatus = convertPlayStateToPlayStatus(state);
462f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu
463f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) &&
464f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu            (mCurrentPlayState != state) && oldPosValid) {
465f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu            mCurrentPosMs = getPlayPosition();
466f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        }
467f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu
468c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mCurrentPlayState = state;
469f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) {
470f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu            mCurrentPosMs = currentPosMs;
471f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        }
472aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        if (state == RemoteControlClient.PLAYSTATE_PLAYING) {
473aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            mPlayStartTimeMs = SystemClock.elapsedRealtime();
474aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        }
475aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
476f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        boolean newPosValid = (mCurrentPosMs !=
477f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu                               RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
478f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        long playPosition = getPlayPosition();
479aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
480aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        /* need send play position changed notification when play status is changed */
481aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) &&
482aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) ||
483f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu             (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) {
484aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
485f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu            registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition);
486aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        }
487aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid &&
488aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            (state == RemoteControlClient.PLAYSTATE_PLAYING)) {
489aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
490f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu            mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition);
491aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        }
492aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
493c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) {
494c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
495c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
496c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
497c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
498c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
499c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void updateTransportControls(int transportControlFlags) {
500c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mTransportControlFlags = transportControlFlags;
501c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
502c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
503c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    class Metadata {
504c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        private String artist;
505c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        private String trackTitle;
506c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        private String albumTitle;
507c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
508c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public Metadata() {
509c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            artist = null;
510c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            trackTitle = null;
511c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            albumTitle = null;
512c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
513c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
514c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public String toString() {
515c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" +
516c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                   albumTitle + "]";
517c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
518c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
519c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
520c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private String getMdString(Bundle data, int id) {
521c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        return data.getString(Integer.toString(id));
522c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
523c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
524aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long getMdLong(Bundle data, int id) {
525aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        return data.getLong(Integer.toString(id));
526aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    }
527aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
528c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void updateMetadata(Bundle data) {
529c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        String oldMetadata = mMetadata.toString();
530c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
531c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE);
532c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM);
533c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (!oldMetadata.equals(mMetadata.toString())) {
534c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            mTrackNumber++;
535c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
536c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
537c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                sendTrackChangedRsp();
538c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
539aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
540f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu            if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
541aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mCurrentPosMs = 0L;
542aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
543aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                    mPlayStartTimeMs = SystemClock.elapsedRealtime();
544aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                }
545aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            }
546aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            /* need send play position changed notification when track is changed */
547aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) {
548aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
549aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                registerNotificationRspPlayPosNative(mPlayPosChangedNT,
550aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                                                     (int)getPlayPosition());
551aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
552aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            }
553c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
554c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (DEBUG) Log.v(TAG, "mMetadata=" + mMetadata.toString());
555aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
556aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mSongLengthMs = getMdLong(data, MediaMetadataRetriever.METADATA_KEY_DURATION);
557aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        if (DEBUG) Log.v(TAG, "duration=" + mSongLengthMs);
558c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
559c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
56017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private void getRcFeatures(byte[] address, int features) {
56117675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0,
56217675906064bb72fdcca75baa56cdf8bb8968d01John Du                                             Utils.getAddressStringFromByte(address));
56317675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
56417675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
56517675906064bb72fdcca75baa56cdf8bb8968d01John Du
566c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void getPlayStatus() {
567c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
568c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.sendMessage(msg);
569c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
570c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
571c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void getElementAttr(byte numAttr, int[] attrs) {
572c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        int i;
573c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        ArrayList<Integer> attrList = new ArrayList<Integer>();
574c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        for (i = 0; i < numAttr; ++i) {
575c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            attrList.add(attrs[i]);
576c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
577c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, (int)numAttr, 0, attrList);
578c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.sendMessage(msg);
579c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
580c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
581c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void registerNotification(int eventId, int param) {
582c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
583c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.sendMessage(msg);
584c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
585c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
586c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void processRegisterNotification(int eventId, int param) {
587c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        switch (eventId) {
588c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case EVT_PLAY_STATUS_CHANGED:
589c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
590c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
591c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                                       convertPlayStateToPlayStatus(mCurrentPlayState));
592c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
593c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
594c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case EVT_TRACK_CHANGED:
595c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
596c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                sendTrackChangedRsp();
597c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
598c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
599aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            case EVT_PLAY_POS_CHANGED:
600aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                long songPosition = getPlayPosition();
601aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
602aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mPlaybackIntervalMs = (long)param * 1000L;
603f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu                if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
604aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                    mNextPosMs = songPosition + mPlaybackIntervalMs;
605aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                    mPrevPosMs = songPosition - mPlaybackIntervalMs;
606aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                    if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
607aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                        Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
608aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                        mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs);
609aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                    }
610aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                }
611aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition);
612aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                break;
613aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
614c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
615c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
616c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
617ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void handlePassthroughCmd(int id, int keyState) {
618ace834feb02adabd61f628c4471147aea02d939cJohn Du        switch (id) {
619ace834feb02adabd61f628c4471147aea02d939cJohn Du            case AVRC_ID_REWIND:
620ace834feb02adabd61f628c4471147aea02d939cJohn Du                rewind(keyState);
621ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
622ace834feb02adabd61f628c4471147aea02d939cJohn Du            case AVRC_ID_FAST_FOR:
623ace834feb02adabd61f628c4471147aea02d939cJohn Du                fastForward(keyState);
624ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
625ace834feb02adabd61f628c4471147aea02d939cJohn Du        }
626ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
627ace834feb02adabd61f628c4471147aea02d939cJohn Du
628ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void fastForward(int keyState) {
629ace834feb02adabd61f628c4471147aea02d939cJohn Du        Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0);
630ace834feb02adabd61f628c4471147aea02d939cJohn Du        mHandler.sendMessage(msg);
631ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
632ace834feb02adabd61f628c4471147aea02d939cJohn Du
633ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void rewind(int keyState) {
634ace834feb02adabd61f628c4471147aea02d939cJohn Du        Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0);
635ace834feb02adabd61f628c4471147aea02d939cJohn Du        mHandler.sendMessage(msg);
636ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
637ace834feb02adabd61f628c4471147aea02d939cJohn Du
638ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void changePositionBy(long amount) {
639ace834feb02adabd61f628c4471147aea02d939cJohn Du        long currentPosMs = getPlayPosition();
640ace834feb02adabd61f628c4471147aea02d939cJohn Du        if (currentPosMs == -1L) return;
641ace834feb02adabd61f628c4471147aea02d939cJohn Du        long newPosMs = Math.max(0L, currentPosMs + amount);
642ace834feb02adabd61f628c4471147aea02d939cJohn Du        mAudioManager.setRemoteControlClientPlaybackPosition(mClientGeneration,
643ace834feb02adabd61f628c4471147aea02d939cJohn Du                newPosMs);
644ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
645ace834feb02adabd61f628c4471147aea02d939cJohn Du
646ace834feb02adabd61f628c4471147aea02d939cJohn Du    private int getSkipMultiplier() {
647ace834feb02adabd61f628c4471147aea02d939cJohn Du        long currentTime = SystemClock.elapsedRealtime();
648ace834feb02adabd61f628c4471147aea02d939cJohn Du        return (int) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
649ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
650ace834feb02adabd61f628c4471147aea02d939cJohn Du
651c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void sendTrackChangedRsp() {
652c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        byte[] track = new byte[TRACK_ID_SIZE];
65379d176b0f3d6cb33c7e52be6641fd4808ba87e93Zhihai Xu        /* track is stored in big endian format */
654c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        for (int i = 0; i < TRACK_ID_SIZE; ++i) {
65579d176b0f3d6cb33c7e52be6641fd4808ba87e93Zhihai Xu            track[i] = (byte) (mTrackNumber >> (56 - 8 * i));
656c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
657c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
658c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
659c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
660aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long getPlayPosition() {
661aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        long songPosition = -1L;
662f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu        if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
663aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
664aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                songPosition = SystemClock.elapsedRealtime() -
665aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                               mPlayStartTimeMs + mCurrentPosMs;
666aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            } else {
667aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                songPosition = mCurrentPosMs;
668aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            }
669aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        }
670aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        if (DEBUG) Log.v(TAG, "position=" + songPosition);
671aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        return songPosition;
672aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    }
673aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
674c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private String getAttributeString(int attrId) {
675c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        String attrStr = null;
676c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        switch (attrId) {
677c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MEDIA_ATTR_TITLE:
678c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                attrStr = mMetadata.trackTitle;
679c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
680c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
681c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MEDIA_ATTR_ARTIST:
682c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                attrStr = mMetadata.artist;
683c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
684c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
685c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MEDIA_ATTR_ALBUM:
686c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                attrStr = mMetadata.albumTitle;
687c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
688c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
689aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            case MEDIA_ATTR_PLAYING_TIME:
690aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                if (mSongLengthMs != 0L) {
691aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                    attrStr = Long.toString(mSongLengthMs);
692aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                }
693aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                break;
694aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
695c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
696c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (attrStr == null) {
697c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            attrStr = new String();
698c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
699c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (DEBUG) Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr);
700c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        return attrStr;
701c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
702c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
703c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int convertPlayStateToPlayStatus(int playState) {
704c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        int playStatus = PLAYSTATUS_ERROR;
705c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        switch (playState) {
706c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_PLAYING:
707c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_BUFFERING:
708c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_PLAYING;
709c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
710c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
711c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_STOPPED:
71279d176b0f3d6cb33c7e52be6641fd4808ba87e93Zhihai Xu            case RemoteControlClient.PLAYSTATE_NONE:
713c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_STOPPED;
714c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
715c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
716c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_PAUSED:
717c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_PAUSED;
718c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
719c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
720c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
721c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
722c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_FWD_SEEK;
723c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
724c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
725c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_REWINDING:
726c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
727c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_REV_SEEK;
728c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
729c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
730c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case RemoteControlClient.PLAYSTATE_ERROR:
731c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_ERROR;
732c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
733c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
734c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
735c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        return playStatus;
736c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
737c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
73817675906064bb72fdcca75baa56cdf8bb8968d01John Du    /**
73917675906064bb72fdcca75baa56cdf8bb8968d01John Du     * This is called from AudioService. It will return whether this device supports abs volume.
74017675906064bb72fdcca75baa56cdf8bb8968d01John Du     * NOT USED AT THE MOMENT.
74117675906064bb72fdcca75baa56cdf8bb8968d01John Du     */
74217675906064bb72fdcca75baa56cdf8bb8968d01John Du    public boolean isAbsoluteVolumeSupported() {
74317675906064bb72fdcca75baa56cdf8bb8968d01John Du        return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
74417675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
74517675906064bb72fdcca75baa56cdf8bb8968d01John Du
74617675906064bb72fdcca75baa56cdf8bb8968d01John Du    /**
74717675906064bb72fdcca75baa56cdf8bb8968d01John Du     * We get this call from AudioService. This will send a message to our handler object,
74817675906064bb72fdcca75baa56cdf8bb8968d01John Du     * requesting our handler to call setVolumeNative()
74917675906064bb72fdcca75baa56cdf8bb8968d01John Du     */
75017675906064bb72fdcca75baa56cdf8bb8968d01John Du    public void adjustVolume(int direction) {
75117675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
75217675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
75317675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
75417675906064bb72fdcca75baa56cdf8bb8968d01John Du
75517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private void adjustVolumeImmediate(int direction) {
75617675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
75717675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessageAtFrontOfQueue(msg);
75817675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
75917675906064bb72fdcca75baa56cdf8bb8968d01John Du
76017675906064bb72fdcca75baa56cdf8bb8968d01John Du    public void setAbsoluteVolume(int volume) {
76117675906064bb72fdcca75baa56cdf8bb8968d01John Du        int avrcpVolume = convertToAvrcpVolume(volume);
76217675906064bb72fdcca75baa56cdf8bb8968d01John Du        avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
76317675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.removeMessages(MESSAGE_ADJUST_VOLUME);
76417675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, avrcpVolume, 0);
76517675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
76617675906064bb72fdcca75baa56cdf8bb8968d01John Du
76717675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
76817675906064bb72fdcca75baa56cdf8bb8968d01John Du
76917675906064bb72fdcca75baa56cdf8bb8968d01John Du    /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
77017675906064bb72fdcca75baa56cdf8bb8968d01John Du     * case when the volume is change locally on the carkit. This notification is not called when
77117675906064bb72fdcca75baa56cdf8bb8968d01John Du     * the volume is changed from the phone.
77217675906064bb72fdcca75baa56cdf8bb8968d01John Du     *
77317675906064bb72fdcca75baa56cdf8bb8968d01John Du     * This method will send a message to our handler to change the local stored volume and notify
77417675906064bb72fdcca75baa56cdf8bb8968d01John Du     * AudioService to update the UI
77517675906064bb72fdcca75baa56cdf8bb8968d01John Du     */
77617675906064bb72fdcca75baa56cdf8bb8968d01John Du    private void volumeChangeCallback(int volume, int ctype) {
77717675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype);
77817675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
77917675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
78017675906064bb72fdcca75baa56cdf8bb8968d01John Du
78117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private void notifyVolumeChanged(int oldVolume, int volume) {
78217675906064bb72fdcca75baa56cdf8bb8968d01John Du        oldVolume = convertToAudioStreamVolume(oldVolume);
78317675906064bb72fdcca75baa56cdf8bb8968d01John Du        volume = convertToAudioStreamVolume(volume);
78417675906064bb72fdcca75baa56cdf8bb8968d01John Du        mAudioManager.avrcpUpdateVolume(oldVolume, volume);
78517675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
78617675906064bb72fdcca75baa56cdf8bb8968d01John Du
78717675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int convertToAudioStreamVolume(int volume) {
78817675906064bb72fdcca75baa56cdf8bb8968d01John Du        // Rescale volume to match AudioSystem's volume
78917675906064bb72fdcca75baa56cdf8bb8968d01John Du        return (int) Math.ceil((double) volume*AUDIO_STREAM_MAX_VOL/AVRCP_MAX_VOL);
79017675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
79117675906064bb72fdcca75baa56cdf8bb8968d01John Du
79217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int convertToAvrcpVolume(int volume) {
79317675906064bb72fdcca75baa56cdf8bb8968d01John Du        return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/AUDIO_STREAM_MAX_VOL);
79417675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
79517675906064bb72fdcca75baa56cdf8bb8968d01John Du
796c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // Do not modify without updating the HAL bt_rc.h files.
797c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
798c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_play_status_t enum of bt_rc.h
799c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_STOPPED = 0;
800c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_PLAYING = 1;
801c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_PAUSED = 2;
802c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_FWD_SEEK = 3;
803c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_REV_SEEK = 4;
804c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_ERROR = 255;
805c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
806c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_media_attr_t enum of bt_rc.h
807c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_TITLE = 1;
808c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_ARTIST = 2;
809c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_ALBUM = 3;
810c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_TRACK_NUM = 4;
811c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_NUM_TRACKS = 5;
812c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_GENRE = 6;
813c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_PLAYING_TIME = 7;
814c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
815c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_event_id_t enum of bt_rc.h
816c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_PLAY_STATUS_CHANGED = 1;
817c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_TRACK_CHANGED = 2;
818c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_TRACK_REACHED_END = 3;
819c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_TRACK_REACHED_START = 4;
820c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_PLAY_POS_CHANGED = 5;
821c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_BATT_STATUS_CHANGED = 6;
822c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_SYSTEM_STATUS_CHANGED = 7;
823c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_APP_SETTINGS_CHANGED = 8;
824c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
825c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_notification_type_t enum of bt_rc.h
826c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int NOTIFICATION_TYPE_INTERIM = 0;
827c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int NOTIFICATION_TYPE_CHANGED = 1;
828c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
829c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with BTRC_UID_SIZE of bt_rc.h
830c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int TRACK_ID_SIZE = 8;
831c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
832c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native static void classInitNative();
833c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native void initNative();
834c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native void cleanupNative();
835c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
836c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
837c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
838c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
839aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
84017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private native boolean setVolumeNative(int volume);
841c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu}
842