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
17066ad9e16a548218b139424f758b92db7af34af2Mike Lockwoodpackage com.android.bluetooth.avrcp;
18c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
19ace834feb02adabd61f628c4471147aea02d939cJohn Duimport java.util.Timer;
20ace834feb02adabd61f628c4471147aea02d939cJohn Duimport java.util.TimerTask;
21ace834feb02adabd61f628c4471147aea02d939cJohn Du
22188f205b5f093850d4cc627917a21204be36c56aZhihai Xuimport android.bluetooth.BluetoothA2dp;
23066ad9e16a548218b139424f758b92db7af34af2Mike Lockwoodimport android.bluetooth.BluetoothAvrcp;
24c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.content.Context;
25aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xuimport android.content.Intent;
2611798b011c962b602217b479130d413f3b30f19aLiejun Taoimport android.content.res.Resources;
2711798b011c962b602217b479130d413f3b30f19aLiejun Taoimport android.content.SharedPreferences;
28c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.graphics.Bitmap;
29c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.media.AudioManager;
300fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssenimport android.media.MediaDescription;
312fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssenimport android.media.MediaMetadata;
322fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssenimport android.media.session.MediaController;
332fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssenimport android.media.session.MediaSessionManager;
342fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssenimport android.media.session.PlaybackState;
35c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Bundle;
36c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Handler;
37c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.HandlerThread;
38c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Looper;
39c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.Message;
40c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.ParcelUuid;
41c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.PowerManager;
42c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.PowerManager.WakeLock;
43c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.RemoteException;
44c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.os.ServiceManager;
45aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xuimport android.os.SystemClock;
46c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport android.util.Log;
47881675b362bde18acbbcf69c513175addca4a8baZhenye Zhuimport android.view.KeyEvent;
486e29e12add362546784126119f26f04fc760f021RoboErik
4911798b011c962b602217b479130d413f3b30f19aLiejun Taoimport com.android.bluetooth.R;
50c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.bluetooth.btservice.AdapterService;
51c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.bluetooth.btservice.ProfileService;
52aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xuimport com.android.bluetooth.Utils;
53c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.internal.util.IState;
54c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.internal.util.State;
55c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport com.android.internal.util.StateMachine;
566e29e12add362546784126119f26f04fc760f021RoboErik
57c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.lang.ref.WeakReference;
58c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.util.ArrayList;
5911798b011c962b602217b479130d413f3b30f19aLiejun Taoimport java.util.HashMap;
60c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.util.List;
61c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xuimport java.util.Set;
62c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu/**
63c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * support Bluetooth AVRCP profile.
64c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu * support metadata, play status and event notification
65c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu */
66066ad9e16a548218b139424f758b92db7af34af2Mike Lockwoodpublic final class Avrcp {
678e0d927632cce4ad53df85fb7627b915c5fbd64bRoboErik    private static final boolean DEBUG = false;
68c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private static final String TAG = "Avrcp";
6911798b011c962b602217b479130d413f3b30f19aLiejun Tao    private static final String ABSOLUTE_VOLUME_BLACKLIST = "absolute_volume_blacklist";
70c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
71c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private Context mContext;
72c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private final AudioManager mAudioManager;
73c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private AvrcpMessageHandler mHandler;
742fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private MediaSessionManager mMediaSessionManager;
752fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private MediaSessionChangeListener mSessionChangeListener;
762fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private MediaController mMediaController;
772fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private MediaControllerListener mMediaControllerCb;
780fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen    private MediaAttributes mMediaAttributes;
79c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mTransportControlFlags;
802fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private PlaybackState mCurrentPlayState;
81eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen    private long mLastStateUpdate;
82c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mPlayStatusChangedNT;
83c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private int mTrackChangedNT;
84eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen    private int mPlayPosChangedNT;
85c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private long mTrackNumber;
86aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mSongLengthMs;
87aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mPlaybackIntervalMs;
887d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen    private long mLastReportedPosition;
89aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mNextPosMs;
90aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long mPrevPosMs;
91ace834feb02adabd61f628c4471147aea02d939cJohn Du    private long mSkipStartTime;
9217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mFeatures;
9311798b011c962b602217b479130d413f3b30f19aLiejun Tao    private int mRemoteVolume;
9411798b011c962b602217b479130d413f3b30f19aLiejun Tao    private int mLastRemoteVolume;
9511798b011c962b602217b479130d413f3b30f19aLiejun Tao    private int mInitialRemoteVolume;
9611798b011c962b602217b479130d413f3b30f19aLiejun Tao
9711798b011c962b602217b479130d413f3b30f19aLiejun Tao    /* Local volume in audio index 0-15 */
9811798b011c962b602217b479130d413f3b30f19aLiejun Tao    private int mLocalVolume;
9911798b011c962b602217b479130d413f3b30f19aLiejun Tao    private int mLastLocalVolume;
10011798b011c962b602217b479130d413f3b30f19aLiejun Tao    private int mAbsVolThreshold;
10111798b011c962b602217b479130d413f3b30f19aLiejun Tao
10211798b011c962b602217b479130d413f3b30f19aLiejun Tao    private String mAddress;
10311798b011c962b602217b479130d413f3b30f19aLiejun Tao    private HashMap<Integer, Integer> mVolumeMapping;
10411798b011c962b602217b479130d413f3b30f19aLiejun Tao
10517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mLastDirection;
1062e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie    private final int mVolumeStep;
1072e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie    private final int mAudioStreamMax;
10811798b011c962b602217b479130d413f3b30f19aLiejun Tao    private boolean mVolCmdAdjustInProgress;
10911798b011c962b602217b479130d413f3b30f19aLiejun Tao    private boolean mVolCmdSetInProgress;
11017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int mAbsVolRetryTimes;
11119e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu    private int mSkipAmount;
112ace834feb02adabd61f628c4471147aea02d939cJohn Du
11317675906064bb72fdcca75baa56cdf8bb8968d01John Du    /* BTRC features */
11417675906064bb72fdcca75baa56cdf8bb8968d01John Du    public static final int BTRC_FEAT_METADATA = 0x01;
11517675906064bb72fdcca75baa56cdf8bb8968d01John Du    public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
11617675906064bb72fdcca75baa56cdf8bb8968d01John Du    public static final int BTRC_FEAT_BROWSE = 0x04;
11717675906064bb72fdcca75baa56cdf8bb8968d01John Du
11817675906064bb72fdcca75baa56cdf8bb8968d01John Du    /* AVRC response codes, from avrc_defs */
11917675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_NOT_IMPL = 8;
12017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_ACCEPT = 9;
12117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_REJ = 10;
12217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_IN_TRANS = 11;
12317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_IMPL_STBL = 12;
12417675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_CHANGED = 13;
12517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRC_RSP_INTERIM = 15;
12617675906064bb72fdcca75baa56cdf8bb8968d01John Du
12717675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_GET_RC_FEATURES = 1;
12817675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_GET_PLAY_STATUS = 2;
12917675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_GET_ELEM_ATTRS = 3;
13017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_REGISTER_NOTIFICATION = 4;
13117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5;
13217675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_VOLUME_CHANGED = 6;
13317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_ADJUST_VOLUME = 7;
13417675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8;
13517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_ABS_VOL_TIMEOUT = 9;
13617675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_FAST_FORWARD = 10;
13717675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int MESSAGE_REWIND = 11;
13819e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu    private static final int MESSAGE_CHANGE_PLAY_POS = 12;
139188f205b5f093850d4cc627917a21204be36c56aZhihai Xu    private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13;
140c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
141ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int BUTTON_TIMEOUT_TIME = 2000;
142ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int BASE_SKIP_AMOUNT = 2000;
143ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int KEY_STATE_PRESS = 1;
144ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int KEY_STATE_RELEASE = 0;
145ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int SKIP_PERIOD = 400;
146ace834feb02adabd61f628c4471147aea02d939cJohn Du    private static final int SKIP_DOUBLE_INTERVAL = 3000;
14719e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu    private static final long MAX_MULTIPLIER_VALUE = 128L;
14817675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int CMD_TIMEOUT_DELAY = 2000;
1492583f09407c8265c12d198d408692c75eabf22a3Liejun Tao    private static final int MAX_ERROR_RETRY_TIMES = 6;
15017675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRCP_MAX_VOL = 127;
15117675906064bb72fdcca75baa56cdf8bb8968d01John Du    private static final int AVRCP_BASE_VOLUME_STEP = 1;
152ace834feb02adabd61f628c4471147aea02d939cJohn Du
153c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    static {
154c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        classInitNative();
155c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
156c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
157c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private Avrcp(Context context) {
1580fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        mMediaAttributes = new MediaAttributes(null);
1592fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
160c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
161c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
16279d176b0f3d6cb33c7e52be6641fd4808ba87e93Zhihai Xu        mTrackNumber = -1L;
163eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        mLastStateUpdate = -1L;
164aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mSongLengthMs = 0L;
165aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mPlaybackIntervalMs = 0L;
166aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
1677d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        mLastReportedPosition = -1;
16884d3f084d4d1a65eb7a481e5a7fe4a6ac827be7bMarie Janssen        mNextPosMs = -1;
16984d3f084d4d1a65eb7a481e5a7fe4a6ac827be7bMarie Janssen        mPrevPosMs = -1;
17017675906064bb72fdcca75baa56cdf8bb8968d01John Du        mFeatures = 0;
17111798b011c962b602217b479130d413f3b30f19aLiejun Tao        mRemoteVolume = -1;
17211798b011c962b602217b479130d413f3b30f19aLiejun Tao        mInitialRemoteVolume = -1;
17311798b011c962b602217b479130d413f3b30f19aLiejun Tao        mLastRemoteVolume = -1;
17417675906064bb72fdcca75baa56cdf8bb8968d01John Du        mLastDirection = 0;
17511798b011c962b602217b479130d413f3b30f19aLiejun Tao        mVolCmdAdjustInProgress = false;
17611798b011c962b602217b479130d413f3b30f19aLiejun Tao        mVolCmdSetInProgress = false;
17717675906064bb72fdcca75baa56cdf8bb8968d01John Du        mAbsVolRetryTimes = 0;
17811798b011c962b602217b479130d413f3b30f19aLiejun Tao        mLocalVolume = -1;
17911798b011c962b602217b479130d413f3b30f19aLiejun Tao        mLastLocalVolume = -1;
18011798b011c962b602217b479130d413f3b30f19aLiejun Tao        mAbsVolThreshold = 0;
18111798b011c962b602217b479130d413f3b30f19aLiejun Tao        mVolumeMapping = new HashMap<Integer, Integer>();
182c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
183c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mContext = context;
184c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
185c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        initNative();
186c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
1872fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
188c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
1892e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie        mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
1902e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie        mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax);
19111798b011c962b602217b479130d413f3b30f19aLiejun Tao        Resources resources = context.getResources();
19211798b011c962b602217b479130d413f3b30f19aLiejun Tao        if (resources != null) {
19311798b011c962b602217b479130d413f3b30f19aLiejun Tao            mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
19411798b011c962b602217b479130d413f3b30f19aLiejun Tao        }
195c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
196c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
197c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void start() {
198c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
199c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        thread.start();
200c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Looper looper = thread.getLooper();
201c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler = new AvrcpMessageHandler(looper);
2022fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen
2032fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mSessionChangeListener = new MediaSessionChangeListener();
2042fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionChangeListener, null, mHandler);
2052fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        List<MediaController> sessions = mMediaSessionManager.getActiveSessions(null);
2062fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mMediaControllerCb = new MediaControllerListener();
2072fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        if (sessions.size() > 0) {
2082fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            updateCurrentMediaController(sessions.get(0));
2092fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        }
210c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
211c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
212066ad9e16a548218b139424f758b92db7af34af2Mike Lockwood    public static Avrcp make(Context context) {
213c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (DEBUG) Log.v(TAG, "make");
214c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Avrcp ar = new Avrcp(context);
215c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        ar.start();
216c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        return ar;
217c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
218c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
219c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    public void doQuit() {
220c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.removeCallbacksAndMessages(null);
221c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Looper looper = mHandler.getLooper();
222c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        if (looper != null) {
223c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            looper.quit();
224c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
2252fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionChangeListener);
226c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
227c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
228c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    public void cleanup() {
229c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        cleanupNative();
23011798b011c962b602217b479130d413f3b30f19aLiejun Tao        if (mVolumeMapping != null)
23111798b011c962b602217b479130d413f3b30f19aLiejun Tao            mVolumeMapping.clear();
232c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
233c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
2342fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private class MediaControllerListener extends MediaController.Callback {
2352fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        @Override
2362fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        public void onMetadataChanged(MediaMetadata metadata) {
2372fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            Log.v(TAG, "MediaController metadata changed");
2382fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            updateMetadata(metadata);
239c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
240c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
241bc10e7d58aa55da25c18d8056a0254a2b736146aZhihai Xu        @Override
2422fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        public void onPlaybackStateChanged(PlaybackState state) {
2432fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            Log.v(TAG, "MediaController playback changed: " + state.toString());
244eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            updatePlaybackState(state);
245c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
246c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
247c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
2482fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        public void onSessionDestroyed() {
2492fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            Log.v(TAG, "MediaController session destroyed");
250c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
2512fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    }
252c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
2532fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener {
2542fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        public MediaSessionChangeListener() {
255c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
256c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
257c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
2582fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        public void onActiveSessionsChanged(List<MediaController> controllers) {
2592fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            Log.v(TAG, "Active sessions changed, " + controllers.size() + " sessions");
2602fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            if (controllers.size() > 0) {
2612fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                updateCurrentMediaController(controllers.get(0));
262c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
263c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
2642fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    }
265c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
2662fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private void updateCurrentMediaController(MediaController controller) {
2672fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        Log.v(TAG, "Updating media controller to " + controller);
2682fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        if (mMediaController != null) {
2692fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            mMediaController.unregisterCallback(mMediaControllerCb);
2702fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        }
2712fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mMediaController = controller;
2722fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        if (mMediaController == null) {
2732fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            updateMetadata(null);
2742fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            return;
275c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
2762fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mMediaController.registerCallback(mMediaControllerCb, mHandler);
2772fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        updateMetadata(mMediaController.getMetadata());
278c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
279c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
280c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    /** Handles Avrcp messages. */
281c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private final class AvrcpMessageHandler extends Handler {
282c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        private AvrcpMessageHandler(Looper looper) {
283c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            super(looper);
284c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
285c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
286c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        @Override
287c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public void handleMessage(Message msg) {
288c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            switch (msg.what) {
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;
29411798b011c962b602217b479130d413f3b30f19aLiejun Tao                mFeatures = modifyRcFeatureFromBlacklist(mFeatures, address);
29517675906064bb72fdcca75baa56cdf8bb8968d01John Du                mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported());
29611798b011c962b602217b479130d413f3b30f19aLiejun Tao                mLastLocalVolume = -1;
29711798b011c962b602217b479130d413f3b30f19aLiejun Tao                mRemoteVolume = -1;
29811798b011c962b602217b479130d413f3b30f19aLiejun Tao                mLocalVolume = -1;
29911798b011c962b602217b479130d413f3b30f19aLiejun Tao                mInitialRemoteVolume = -1;
30011798b011c962b602217b479130d413f3b30f19aLiejun Tao                mAddress = address;
30111798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (mVolumeMapping != null)
30211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mVolumeMapping.clear();
30317675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
30417675906064bb72fdcca75baa56cdf8bb8968d01John Du
305c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MESSAGE_GET_PLAY_STATUS:
306c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
307aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState),
308aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                                       (int)mSongLengthMs, (int)getPlayPosition());
309c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
310c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
311c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MESSAGE_GET_ELEM_ATTRS:
312c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                String[] textArray;
313c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                int[] attrIds;
314c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                byte numAttr = (byte) msg.arg1;
315c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj;
31665e7943d098113c5aa56e9822b68f0c51c6dbe36Marie Janssen                Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
317c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                attrIds = new int[numAttr];
318c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                textArray = new String[numAttr];
319c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                for (int i = 0; i < numAttr; ++i) {
320c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                    attrIds[i] = attrList.get(i).intValue();
3210fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    textArray[i] = mMediaAttributes.getString(attrIds[i]);
3220fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    Log.v(TAG, "getAttributeString:attrId=" + attrIds[i] +
3230fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                               " str=" + textArray[i]);
324c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                }
325c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                getElementAttrRspNative(numAttr, attrIds, textArray);
326c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
32765e7943d098113c5aa56e9822b68f0c51c6dbe36Marie Janssen
328c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case MESSAGE_REGISTER_NOTIFICATION:
329c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
330c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                                      " param=" + msg.arg2);
331c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                processRegisterNotification(msg.arg1, msg.arg2);
332c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
333c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
334aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            case MESSAGE_PLAY_INTERVAL_TIMEOUT:
335aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
336eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen                sendPlayPosNotificationRsp(false);
337aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                break;
338aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
33917675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_VOLUME_CHANGED:
34011798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (!isAbsoluteVolumeSupported()) {
34111798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (DEBUG) Log.v(TAG, "ignore MESSAGE_VOLUME_CHANGED");
34211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    break;
34311798b011c962b602217b479130d413f3b30f19aLiejun Tao                }
34411798b011c962b602217b479130d413f3b30f19aLiejun Tao
3455c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta                if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f)
3465c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta                                                        + " ctype=" + msg.arg2);
34717675906064bb72fdcca75baa56cdf8bb8968d01John Du
34811798b011c962b602217b479130d413f3b30f19aLiejun Tao
34911798b011c962b602217b479130d413f3b30f19aLiejun Tao                boolean volAdj = false;
35017675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) {
35111798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (mVolCmdAdjustInProgress == false && mVolCmdSetInProgress == false) {
35217675906064bb72fdcca75baa56cdf8bb8968d01John Du                        Log.e(TAG, "Unsolicited response, ignored");
35317675906064bb72fdcca75baa56cdf8bb8968d01John Du                        break;
35417675906064bb72fdcca75baa56cdf8bb8968d01John Du                    }
35517675906064bb72fdcca75baa56cdf8bb8968d01John Du                    removeMessages(MESSAGE_ABS_VOL_TIMEOUT);
35611798b011c962b602217b479130d413f3b30f19aLiejun Tao
35711798b011c962b602217b479130d413f3b30f19aLiejun Tao                    volAdj = mVolCmdAdjustInProgress;
35811798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mVolCmdAdjustInProgress = false;
35911798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mVolCmdSetInProgress = false;
36017675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mAbsVolRetryTimes = 0;
36117675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
36211798b011c962b602217b479130d413f3b30f19aLiejun Tao
36311798b011c962b602217b479130d413f3b30f19aLiejun Tao                byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD
36411798b011c962b602217b479130d413f3b30f19aLiejun Tao                // convert remote volume to local volume
36511798b011c962b602217b479130d413f3b30f19aLiejun Tao                int volIndex = convertToAudioStreamVolume(absVol);
36611798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (mInitialRemoteVolume == -1) {
36711798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mInitialRemoteVolume = absVol;
36811798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax && volIndex > mAbsVolThreshold) {
36911798b011c962b602217b479130d413f3b30f19aLiejun Tao                        if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" + mAbsVolThreshold);
37011798b011c962b602217b479130d413f3b30f19aLiejun Tao                        Message msg1 = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, mAbsVolThreshold , 0);
37111798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mHandler.sendMessage(msg1);
37211798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mRemoteVolume = absVol;
37311798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mLocalVolume = volIndex;
37411798b011c962b602217b479130d413f3b30f19aLiejun Tao                        break;
37511798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
37611798b011c962b602217b479130d413f3b30f19aLiejun Tao                }
37711798b011c962b602217b479130d413f3b30f19aLiejun Tao
3782fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT ||
3792fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                                                 msg.arg2 == AVRC_RSP_CHANGED ||
3802fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                                                 msg.arg2 == AVRC_RSP_INTERIM)) {
38111798b011c962b602217b479130d413f3b30f19aLiejun Tao                    /* If the volume has successfully changed */
38211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mLocalVolume = volIndex;
38311798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (mLastLocalVolume != -1 && msg.arg2 == AVRC_RSP_ACCEPT) {
38411798b011c962b602217b479130d413f3b30f19aLiejun Tao                        if (mLastLocalVolume != volIndex) {
38511798b011c962b602217b479130d413f3b30f19aLiejun Tao                            /* remote volume changed more than requested due to
38611798b011c962b602217b479130d413f3b30f19aLiejun Tao                             * local and remote has different volume steps */
38711798b011c962b602217b479130d413f3b30f19aLiejun Tao                            if (DEBUG) Log.d(TAG, "Remote returned volume does not match desired volume "
38811798b011c962b602217b479130d413f3b30f19aLiejun Tao                                + mLastLocalVolume + " vs "
38911798b011c962b602217b479130d413f3b30f19aLiejun Tao                                + volIndex);
39011798b011c962b602217b479130d413f3b30f19aLiejun Tao                            mLastLocalVolume = mLocalVolume;
39111798b011c962b602217b479130d413f3b30f19aLiejun Tao                        }
39211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
39311798b011c962b602217b479130d413f3b30f19aLiejun Tao                    // remember the remote volume value, as it's the one supported by remote
39411798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (volAdj) {
39511798b011c962b602217b479130d413f3b30f19aLiejun Tao                        synchronized (mVolumeMapping) {
39611798b011c962b602217b479130d413f3b30f19aLiejun Tao                            mVolumeMapping.put(volIndex, (int)absVol);
39711798b011c962b602217b479130d413f3b30f19aLiejun Tao                            if (DEBUG) Log.v(TAG, "remember volume mapping " +volIndex+ "-"+absVol);
39811798b011c962b602217b479130d413f3b30f19aLiejun Tao                        }
39911798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
40011798b011c962b602217b479130d413f3b30f19aLiejun Tao
40111798b011c962b602217b479130d413f3b30f19aLiejun Tao                    notifyVolumeChanged(mLocalVolume);
40211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mRemoteVolume = absVol;
4035c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta                    long pecentVolChanged = ((long)absVol * 100) / 0x7f;
4045c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta                    Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%");
40517675906064bb72fdcca75baa56cdf8bb8968d01John Du                } else if (msg.arg2 == AVRC_RSP_REJ) {
40617675906064bb72fdcca75baa56cdf8bb8968d01John Du                    Log.e(TAG, "setAbsoluteVolume call rejected");
40711798b011c962b602217b479130d413f3b30f19aLiejun Tao                } else if (volAdj && mLastRemoteVolume > 0 && mLastRemoteVolume < AVRCP_MAX_VOL &&
4082fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                        mLocalVolume == volIndex &&
4092fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                        (msg.arg2 == AVRC_RSP_ACCEPT )) {
41011798b011c962b602217b479130d413f3b30f19aLiejun Tao                    /* oops, the volume is still same, remote does not like the value
41111798b011c962b602217b479130d413f3b30f19aLiejun Tao                     * retry a volume one step up/down */
41211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (DEBUG) Log.d(TAG, "Remote device didn't tune volume, let's try one more step.");
41311798b011c962b602217b479130d413f3b30f19aLiejun Tao                    int retry_volume = Math.min(AVRCP_MAX_VOL,
41411798b011c962b602217b479130d413f3b30f19aLiejun Tao                            Math.max(0, mLastRemoteVolume + mLastDirection));
41511798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (setVolumeNative(retry_volume)) {
41611798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mLastRemoteVolume = retry_volume;
41711798b011c962b602217b479130d413f3b30f19aLiejun Tao                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
41811798b011c962b602217b479130d413f3b30f19aLiejun Tao                                           CMD_TIMEOUT_DELAY);
41911798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mVolCmdAdjustInProgress = true;
42011798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
42117675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
42217675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
42317675906064bb72fdcca75baa56cdf8bb8968d01John Du
42417675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_ADJUST_VOLUME:
42511798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (!isAbsoluteVolumeSupported()) {
42611798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (DEBUG) Log.v(TAG, "ignore MESSAGE_ADJUST_VOLUME");
42711798b011c962b602217b479130d413f3b30f19aLiejun Tao                    break;
42811798b011c962b602217b479130d413f3b30f19aLiejun Tao                }
42911798b011c962b602217b479130d413f3b30f19aLiejun Tao
43017675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1);
43111798b011c962b602217b479130d413f3b30f19aLiejun Tao
43211798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (mVolCmdAdjustInProgress || mVolCmdSetInProgress) {
43317675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
43417675906064bb72fdcca75baa56cdf8bb8968d01John Du                    break;
43517675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
43611798b011c962b602217b479130d413f3b30f19aLiejun Tao
43711798b011c962b602217b479130d413f3b30f19aLiejun Tao                // Remote device didn't set initial volume. Let's black list it
43811798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (mInitialRemoteVolume == -1) {
43911798b011c962b602217b479130d413f3b30f19aLiejun Tao                    Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
44011798b011c962b602217b479130d413f3b30f19aLiejun Tao                    blackListCurrentDevice();
44111798b011c962b602217b479130d413f3b30f19aLiejun Tao                    break;
44211798b011c962b602217b479130d413f3b30f19aLiejun Tao                }
44311798b011c962b602217b479130d413f3b30f19aLiejun Tao
44417675906064bb72fdcca75baa56cdf8bb8968d01John Du                // Wait on verification on volume from device, before changing the volume.
44511798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (mRemoteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) {
44611798b011c962b602217b479130d413f3b30f19aLiejun Tao                    int setVol = -1;
44711798b011c962b602217b479130d413f3b30f19aLiejun Tao                    int targetVolIndex = -1;
44811798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (mLocalVolume == 0 && msg.arg1 == -1) {
44911798b011c962b602217b479130d413f3b30f19aLiejun Tao                        if (DEBUG) Log.w(TAG, "No need to Vol down from 0.");
45011798b011c962b602217b479130d413f3b30f19aLiejun Tao                        break;
45111798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
45211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (mLocalVolume == mAudioStreamMax && msg.arg1 == 1) {
45311798b011c962b602217b479130d413f3b30f19aLiejun Tao                        if (DEBUG) Log.w(TAG, "No need to Vol up from max.");
45411798b011c962b602217b479130d413f3b30f19aLiejun Tao                        break;
45511798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
45611798b011c962b602217b479130d413f3b30f19aLiejun Tao
45711798b011c962b602217b479130d413f3b30f19aLiejun Tao                    targetVolIndex = mLocalVolume + msg.arg1;
45811798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (DEBUG) Log.d(TAG, "Adjusting volume to  " + targetVolIndex);
45911798b011c962b602217b479130d413f3b30f19aLiejun Tao
46011798b011c962b602217b479130d413f3b30f19aLiejun Tao                    Integer i;
46111798b011c962b602217b479130d413f3b30f19aLiejun Tao                    synchronized (mVolumeMapping) {
46211798b011c962b602217b479130d413f3b30f19aLiejun Tao                        i = mVolumeMapping.get(targetVolIndex);
46311798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
46411798b011c962b602217b479130d413f3b30f19aLiejun Tao
46511798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (i != null) {
46611798b011c962b602217b479130d413f3b30f19aLiejun Tao                        /* if we already know this volume mapping, use it */
46711798b011c962b602217b479130d413f3b30f19aLiejun Tao                        setVol = i.byteValue();
46811798b011c962b602217b479130d413f3b30f19aLiejun Tao                        if (setVol == mRemoteVolume) {
46911798b011c962b602217b479130d413f3b30f19aLiejun Tao                            if (DEBUG) Log.d(TAG, "got same volume from mapping for " + targetVolIndex + ", ignore.");
47011798b011c962b602217b479130d413f3b30f19aLiejun Tao                            setVol = -1;
47111798b011c962b602217b479130d413f3b30f19aLiejun Tao                        }
47211798b011c962b602217b479130d413f3b30f19aLiejun Tao                        if (DEBUG) Log.d(TAG, "set volume from mapping " + targetVolIndex + "-" + setVol);
47311798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
47411798b011c962b602217b479130d413f3b30f19aLiejun Tao
47511798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (setVol == -1) {
47611798b011c962b602217b479130d413f3b30f19aLiejun Tao                        /* otherwise use phone steps */
47711798b011c962b602217b479130d413f3b30f19aLiejun Tao                        setVol = Math.min(AVRCP_MAX_VOL,
47811798b011c962b602217b479130d413f3b30f19aLiejun Tao                                 convertToAvrcpVolume(Math.max(0, targetVolIndex)));
47911798b011c962b602217b479130d413f3b30f19aLiejun Tao                        if (DEBUG) Log.d(TAG, "set volume from local volume "+ targetVolIndex+"-"+ setVol);
48011798b011c962b602217b479130d413f3b30f19aLiejun Tao                    }
48111798b011c962b602217b479130d413f3b30f19aLiejun Tao
48217675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (setVolumeNative(setVol)) {
48317675906064bb72fdcca75baa56cdf8bb8968d01John Du                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
48417675906064bb72fdcca75baa56cdf8bb8968d01John Du                                           CMD_TIMEOUT_DELAY);
48511798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mVolCmdAdjustInProgress = true;
48617675906064bb72fdcca75baa56cdf8bb8968d01John Du                        mLastDirection = msg.arg1;
48711798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mLastRemoteVolume = setVol;
48811798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mLastLocalVolume = targetVolIndex;
48911798b011c962b602217b479130d413f3b30f19aLiejun Tao                    } else {
49011798b011c962b602217b479130d413f3b30f19aLiejun Tao                         if (DEBUG) Log.d(TAG, "setVolumeNative failed");
49117675906064bb72fdcca75baa56cdf8bb8968d01John Du                    }
49217675906064bb72fdcca75baa56cdf8bb8968d01John Du                } else {
49317675906064bb72fdcca75baa56cdf8bb8968d01John Du                    Log.e(TAG, "Unknown direction in MESSAGE_ADJUST_VOLUME");
49417675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
49517675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
49617675906064bb72fdcca75baa56cdf8bb8968d01John Du
49717675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_SET_ABSOLUTE_VOLUME:
49811798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (!isAbsoluteVolumeSupported()) {
49911798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (DEBUG) Log.v(TAG, "ignore MESSAGE_SET_ABSOLUTE_VOLUME");
50011798b011c962b602217b479130d413f3b30f19aLiejun Tao                    break;
50111798b011c962b602217b479130d413f3b30f19aLiejun Tao                }
50211798b011c962b602217b479130d413f3b30f19aLiejun Tao
50317675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.v(TAG, "MESSAGE_SET_ABSOLUTE_VOLUME");
50411798b011c962b602217b479130d413f3b30f19aLiejun Tao
50511798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (mVolCmdSetInProgress || mVolCmdAdjustInProgress) {
50617675906064bb72fdcca75baa56cdf8bb8968d01John Du                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
50717675906064bb72fdcca75baa56cdf8bb8968d01John Du                    break;
50817675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
50911798b011c962b602217b479130d413f3b30f19aLiejun Tao
51011798b011c962b602217b479130d413f3b30f19aLiejun Tao                // Remote device didn't set initial volume. Let's black list it
51111798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (mInitialRemoteVolume == -1) {
51211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (DEBUG) Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
51311798b011c962b602217b479130d413f3b30f19aLiejun Tao                    blackListCurrentDevice();
51411798b011c962b602217b479130d413f3b30f19aLiejun Tao                    break;
51511798b011c962b602217b479130d413f3b30f19aLiejun Tao                }
51611798b011c962b602217b479130d413f3b30f19aLiejun Tao
51711798b011c962b602217b479130d413f3b30f19aLiejun Tao                int avrcpVolume = convertToAvrcpVolume(msg.arg1);
51811798b011c962b602217b479130d413f3b30f19aLiejun Tao                avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
5190fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                if (DEBUG) Log.d(TAG, "Setting volume to " + msg.arg1 + "-" + avrcpVolume);
52011798b011c962b602217b479130d413f3b30f19aLiejun Tao                if (setVolumeNative(avrcpVolume)) {
52117675906064bb72fdcca75baa56cdf8bb8968d01John Du                    sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
52211798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mVolCmdSetInProgress = true;
52311798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mLastRemoteVolume = avrcpVolume;
52411798b011c962b602217b479130d413f3b30f19aLiejun Tao                    mLastLocalVolume = msg.arg1;
52511798b011c962b602217b479130d413f3b30f19aLiejun Tao                } else {
52611798b011c962b602217b479130d413f3b30f19aLiejun Tao                     if (DEBUG) Log.d(TAG, "setVolumeNative failed");
52717675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
52817675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
52917675906064bb72fdcca75baa56cdf8bb8968d01John Du
53017675906064bb72fdcca75baa56cdf8bb8968d01John Du            case MESSAGE_ABS_VOL_TIMEOUT:
53117675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (DEBUG) Log.v(TAG, "MESSAGE_ABS_VOL_TIMEOUT: Volume change cmd timed out.");
53211798b011c962b602217b479130d413f3b30f19aLiejun Tao                mVolCmdAdjustInProgress = false;
53311798b011c962b602217b479130d413f3b30f19aLiejun Tao                mVolCmdSetInProgress = false;
53417675906064bb72fdcca75baa56cdf8bb8968d01John Du                if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
53517675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mAbsVolRetryTimes = 0;
5362583f09407c8265c12d198d408692c75eabf22a3Liejun Tao                    /* too many volume change failures, black list the device */
5372583f09407c8265c12d198d408692c75eabf22a3Liejun Tao                    blackListCurrentDevice();
53817675906064bb72fdcca75baa56cdf8bb8968d01John Du                } else {
53917675906064bb72fdcca75baa56cdf8bb8968d01John Du                    mAbsVolRetryTimes += 1;
54011798b011c962b602217b479130d413f3b30f19aLiejun Tao                    if (setVolumeNative(mLastRemoteVolume)) {
54117675906064bb72fdcca75baa56cdf8bb8968d01John Du                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
54217675906064bb72fdcca75baa56cdf8bb8968d01John Du                                           CMD_TIMEOUT_DELAY);
54311798b011c962b602217b479130d413f3b30f19aLiejun Tao                        mVolCmdSetInProgress = true;
54417675906064bb72fdcca75baa56cdf8bb8968d01John Du                    }
54517675906064bb72fdcca75baa56cdf8bb8968d01John Du                }
54617675906064bb72fdcca75baa56cdf8bb8968d01John Du                break;
54717675906064bb72fdcca75baa56cdf8bb8968d01John Du
548ace834feb02adabd61f628c4471147aea02d939cJohn Du            case MESSAGE_FAST_FORWARD:
549ace834feb02adabd61f628c4471147aea02d939cJohn Du            case MESSAGE_REWIND:
5502fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                if (msg.what == MESSAGE_FAST_FORWARD) {
5512fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                    if ((mCurrentPlayState.getActions() &
5522fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                                PlaybackState.ACTION_FAST_FORWARD) != 0) {
5532fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                        int keyState = msg.arg1 == KEY_STATE_PRESS ?
5542fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                                KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
5552fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                        KeyEvent keyEvent =
5562fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                                new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
5572fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                        mMediaController.dispatchMediaButtonEvent(keyEvent);
5582fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                        break;
559881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu                    }
5602fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                } else if ((mCurrentPlayState.getActions() &
5612fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                            PlaybackState.ACTION_REWIND) != 0) {
562881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu                    int keyState = msg.arg1 == KEY_STATE_PRESS ?
5632fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                            KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
564881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu                    KeyEvent keyEvent =
5652fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                            new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND);
5662fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                    mMediaController.dispatchMediaButtonEvent(keyEvent);
567881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu                    break;
568881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu                }
569881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu
57019e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                int skipAmount;
571ace834feb02adabd61f628c4471147aea02d939cJohn Du                if (msg.what == MESSAGE_FAST_FORWARD) {
572ace834feb02adabd61f628c4471147aea02d939cJohn Du                    if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD");
573881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu                    removeMessages(MESSAGE_FAST_FORWARD);
574ace834feb02adabd61f628c4471147aea02d939cJohn Du                    skipAmount = BASE_SKIP_AMOUNT;
575ace834feb02adabd61f628c4471147aea02d939cJohn Du                } else {
576ace834feb02adabd61f628c4471147aea02d939cJohn Du                    if (DEBUG) Log.v(TAG, "MESSAGE_REWIND");
577881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu                    removeMessages(MESSAGE_REWIND);
578ace834feb02adabd61f628c4471147aea02d939cJohn Du                    skipAmount = -BASE_SKIP_AMOUNT;
579ace834feb02adabd61f628c4471147aea02d939cJohn Du                }
580ace834feb02adabd61f628c4471147aea02d939cJohn Du
58119e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                if (hasMessages(MESSAGE_CHANGE_PLAY_POS) &&
58219e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                        (skipAmount != mSkipAmount)) {
58319e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    Log.w(TAG, "missing release button event:" + mSkipAmount);
58419e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                }
58519e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu
58619e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                if ((!hasMessages(MESSAGE_CHANGE_PLAY_POS)) ||
58719e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                        (skipAmount != mSkipAmount)) {
58819e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    mSkipStartTime = SystemClock.elapsedRealtime();
58919e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                }
59019e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu
59119e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                removeMessages(MESSAGE_CHANGE_PLAY_POS);
592ace834feb02adabd61f628c4471147aea02d939cJohn Du                if (msg.arg1 == KEY_STATE_PRESS) {
59319e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    mSkipAmount = skipAmount;
59419e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    changePositionBy(mSkipAmount * getSkipMultiplier());
59519e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
59619e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    posMsg.arg1 = 1;
59719e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    sendMessageDelayed(posMsg, SKIP_PERIOD);
598ace834feb02adabd61f628c4471147aea02d939cJohn Du                }
599881675b362bde18acbbcf69c513175addca4a8baZhenye Zhu
600ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
601ace834feb02adabd61f628c4471147aea02d939cJohn Du
60219e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu            case MESSAGE_CHANGE_PLAY_POS:
60319e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_CHANGE_PLAY_POS:" + msg.arg1);
60419e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                changePositionBy(mSkipAmount * getSkipMultiplier());
60519e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) {
60619e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
60719e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    posMsg.arg1 = msg.arg1 + 1;
60819e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu                    sendMessageDelayed(posMsg, SKIP_PERIOD);
609ace834feb02adabd61f628c4471147aea02d939cJohn Du                }
610ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
611188f205b5f093850d4cc627917a21204be36c56aZhihai Xu
612188f205b5f093850d4cc627917a21204be36c56aZhihai Xu            case MESSAGE_SET_A2DP_AUDIO_STATE:
613188f205b5f093850d4cc627917a21204be36c56aZhihai Xu                if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1);
614188f205b5f093850d4cc627917a21204be36c56aZhihai Xu                updateA2dpAudioState(msg.arg1);
615188f205b5f093850d4cc627917a21204be36c56aZhihai Xu                break;
616c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
617c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
618c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
619c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
620188f205b5f093850d4cc627917a21204be36c56aZhihai Xu    private void updateA2dpAudioState(int state) {
621188f205b5f093850d4cc627917a21204be36c56aZhihai Xu        boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
622188f205b5f093850d4cc627917a21204be36c56aZhihai Xu        if (isPlaying != isPlayingState(mCurrentPlayState)) {
62322bad7047263a6924423d12718738fdcc7b0352cRavi Nagarajan            /* if a2dp is streaming, check to make sure music is active */
6242fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            if (isPlaying && !mAudioManager.isMusicActive())
62522bad7047263a6924423d12718738fdcc7b0352cRavi Nagarajan                return;
6262fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            PlaybackState.Builder builder = new PlaybackState.Builder();
6272fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            if (isPlaying) {
6282fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                builder.setState(PlaybackState.STATE_PLAYING,
6292fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                                 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
6302fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            } else {
6312fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                builder.setState(PlaybackState.STATE_PAUSED,
6322fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                                 PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
6332fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            }
634eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            updatePlaybackState(builder.build());
635188f205b5f093850d4cc627917a21204be36c56aZhihai Xu        }
636188f205b5f093850d4cc627917a21204be36c56aZhihai Xu    }
637188f205b5f093850d4cc627917a21204be36c56aZhihai Xu
638eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen    private void updatePlaybackState(PlaybackState state) {
6392fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        if (state == null) {
6402fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen          state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE,
6412fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                         PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build();
6422fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        }
643eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
6447d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
645c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        int newPlayStatus = convertPlayStateToPlayStatus(state);
646f56b7b37a156d9207074af80c156db5884bb8c08Zhihai Xu
6477d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        if (DEBUG) {
6487d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            Log.v(TAG, "updatePlaybackState (" + mPlayStatusChangedNT + "): "+
6497d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen                       "old=" + mCurrentPlayState + "(" + oldPlayStatus + "), "+
6507d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen                       "new=" + state + "(" + newPlayStatus + ")");
6517d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        }
6527d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen
653aa6c1cb7f08a5d1fe2c878b587c62cf4dbb6ee8fZhihai Xu        mCurrentPlayState = state;
654eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        mLastStateUpdate = SystemClock.elapsedRealtime();
655aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
656eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        sendPlayPosNotificationRsp(false);
657aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
6587d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        if (mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM &&
6597d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            (oldPlayStatus != newPlayStatus)) {
660c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
661c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
662c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
663c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
664c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
665c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void updateTransportControls(int transportControlFlags) {
666c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mTransportControlFlags = transportControlFlags;
667c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
668c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
6690fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen    class MediaAttributes {
6700fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private boolean exists;
6710fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String title;
6720fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String artistName;
6730fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String albumName;
6740fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String mediaNumber;
6750fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String mediaTotalNumber;
6760fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String genre;
6770fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String playingTimeMs;
6780fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
6790fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private static final int ATTR_TITLE = 1;
6800fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private static final int ATTR_ARTIST_NAME = 2;
6810fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private static final int ATTR_ALBUM_NAME = 3;
6820fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private static final int ATTR_MEDIA_NUMBER = 4;
6830fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private static final int ATTR_MEDIA_TOTAL_NUMBER = 5;
6840fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private static final int ATTR_GENRE = 6;
6850fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private static final int ATTR_PLAYING_TIME_MS = 7;
6860fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
6870fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
6880fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        public MediaAttributes(MediaMetadata data) {
6890fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            exists = data != null;
6900fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (!exists)
6910fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                return;
6920fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
6930fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            artistName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ARTIST));
6940fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            albumName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ALBUM));
6950fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            mediaNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
6960fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            mediaTotalNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
6970fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            genre = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_GENRE));
6980fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            playingTimeMs = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_DURATION));
6990fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7000fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            // Try harder for the title.
7010fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            title = data.getString(MediaMetadata.METADATA_KEY_TITLE);
7020fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7030fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (title == null) {
7040fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                MediaDescription desc = data.getDescription();
7050fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                if (desc != null) {
7060fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    CharSequence val = desc.getDescription();
7070fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    if (val != null)
7080fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                        title = val.toString();
7090fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                }
7100fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            }
7110fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7120fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (title == null)
7130fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                title = new String();
7140fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        }
7150fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7160fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        public boolean equals(MediaAttributes other) {
7170fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (other == null)
7180fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                return false;
7190fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7200fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (exists != other.exists)
7210fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                return false;
7220fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7230fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (exists == false)
7240fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                return true;
7250fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7260fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            return (title.equals(other.title)) &&
7270fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                (artistName.equals(other.artistName)) &&
7280fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                (albumName.equals(other.albumName)) &&
7290fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                (mediaNumber.equals(other.mediaNumber)) &&
7300fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                (mediaTotalNumber.equals(other.mediaTotalNumber)) &&
7310fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                (genre.equals(other.genre)) &&
7320fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                (playingTimeMs.equals(other.playingTimeMs));
7330fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        }
7340fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7350fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        public String getString(int attrId) {
7360fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (!exists)
7370fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                return new String();
7380fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7390fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            switch (attrId) {
7400fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                case ATTR_TITLE:
7410fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return title;
7420fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                case ATTR_ARTIST_NAME:
7430fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return artistName;
7440fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                case ATTR_ALBUM_NAME:
7450fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return albumName;
7460fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                case ATTR_MEDIA_NUMBER:
7470fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return mediaNumber;
7480fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                case ATTR_MEDIA_TOTAL_NUMBER:
7490fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return mediaTotalNumber;
7500fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                case ATTR_GENRE:
7510fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return genre;
7520fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                case ATTR_PLAYING_TIME_MS:
7530fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return playingTimeMs;
7540fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                default:
7550fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                    return new String();
7560fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            }
7570fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        }
7580fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7590fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String stringOrBlank(String s) {
7600fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            return s == null ? new String() : s;
7610fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        }
762c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
7630fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        private String longStringOrBlank(Long s) {
7640fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            return s == null ? new String() : s.toString();
765c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
766c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
767c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        public String toString() {
7680fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            if (!exists)
7690fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                return "[MediaAttributes: none]";
7700fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen
7710fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            return "[MediaAttributes: " + title + " - " + albumName + " by "
7720fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                + artistName + " (" + mediaNumber + "/" + mediaTotalNumber + ") "
7730fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen                + genre + "]";
774c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
775c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
776c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
7772fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private void updateMetadata(MediaMetadata data) {
7780fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        MediaAttributes oldAttributes = mMediaAttributes;
7790fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        mMediaAttributes = new MediaAttributes(data);
7802fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        if (data == null) {
7812fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            mSongLengthMs = 0L;
7822fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        } else {
7832fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            mSongLengthMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION);
7842fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        }
7850fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        if (!oldAttributes.equals(mMediaAttributes)) {
7860fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString());
787c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            mTrackNumber++;
788eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
789c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
790c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
791c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                sendTrackChangedRsp();
792c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            }
79365e7943d098113c5aa56e9822b68f0c51c6dbe36Marie Janssen        } else {
7940fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen            Log.v(TAG, "Updated " + mMediaAttributes.toString() + " but no change!");
795c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
796aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
7977d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        // Update the play state, which sends play state and play position
7987d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        // notifications if needed.
7997d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        if (mMediaController != null) {
8007d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen          updatePlaybackState(mMediaController.getPlaybackState());
8017d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        } else {
8027d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen          updatePlaybackState(null);
8037d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        }
804c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
805c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
80617675906064bb72fdcca75baa56cdf8bb8968d01John Du    private void getRcFeatures(byte[] address, int features) {
80717675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0,
80817675906064bb72fdcca75baa56cdf8bb8968d01John Du                                             Utils.getAddressStringFromByte(address));
80917675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
81017675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
81117675906064bb72fdcca75baa56cdf8bb8968d01John Du
812c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void getPlayStatus() {
813c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
814c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.sendMessage(msg);
815c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
816c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
817c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void getElementAttr(byte numAttr, int[] attrs) {
818c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        int i;
819c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        ArrayList<Integer> attrList = new ArrayList<Integer>();
820c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        for (i = 0; i < numAttr; ++i) {
821c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            attrList.add(attrs[i]);
822c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
8236e29e12add362546784126119f26f04fc760f021RoboErik        Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList);
824c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.sendMessage(msg);
825c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
826c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
827c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void registerNotification(int eventId, int param) {
828c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
829c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        mHandler.sendMessage(msg);
830c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
831c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
832c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void processRegisterNotification(int eventId, int param) {
833c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        switch (eventId) {
834c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case EVT_PLAY_STATUS_CHANGED:
835c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
836c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
8372fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                        convertPlayStateToPlayStatus(mCurrentPlayState));
838c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
839c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
840c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu            case EVT_TRACK_CHANGED:
841c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
842c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                sendTrackChangedRsp();
843c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
844c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
845aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu            case EVT_PLAY_POS_CHANGED:
846aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
847aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                mPlaybackIntervalMs = (long)param * 1000L;
8487d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen                sendPlayPosNotificationRsp(true);
849aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu                break;
850aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
851c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
852c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
853c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
854ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void handlePassthroughCmd(int id, int keyState) {
855ace834feb02adabd61f628c4471147aea02d939cJohn Du        switch (id) {
856066ad9e16a548218b139424f758b92db7af34af2Mike Lockwood            case BluetoothAvrcp.PASSTHROUGH_ID_REWIND:
857ace834feb02adabd61f628c4471147aea02d939cJohn Du                rewind(keyState);
858ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
859066ad9e16a548218b139424f758b92db7af34af2Mike Lockwood            case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR:
860ace834feb02adabd61f628c4471147aea02d939cJohn Du                fastForward(keyState);
861ace834feb02adabd61f628c4471147aea02d939cJohn Du                break;
862ace834feb02adabd61f628c4471147aea02d939cJohn Du        }
863ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
864ace834feb02adabd61f628c4471147aea02d939cJohn Du
865ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void fastForward(int keyState) {
866ace834feb02adabd61f628c4471147aea02d939cJohn Du        Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0);
867ace834feb02adabd61f628c4471147aea02d939cJohn Du        mHandler.sendMessage(msg);
868ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
869ace834feb02adabd61f628c4471147aea02d939cJohn Du
870ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void rewind(int keyState) {
871ace834feb02adabd61f628c4471147aea02d939cJohn Du        Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0);
872ace834feb02adabd61f628c4471147aea02d939cJohn Du        mHandler.sendMessage(msg);
873ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
874ace834feb02adabd61f628c4471147aea02d939cJohn Du
875ace834feb02adabd61f628c4471147aea02d939cJohn Du    private void changePositionBy(long amount) {
876ace834feb02adabd61f628c4471147aea02d939cJohn Du        long currentPosMs = getPlayPosition();
877ace834feb02adabd61f628c4471147aea02d939cJohn Du        if (currentPosMs == -1L) return;
878ace834feb02adabd61f628c4471147aea02d939cJohn Du        long newPosMs = Math.max(0L, currentPosMs + amount);
8792fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        mMediaController.getTransportControls().seekTo(newPosMs);
880ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
881ace834feb02adabd61f628c4471147aea02d939cJohn Du
882ace834feb02adabd61f628c4471147aea02d939cJohn Du    private int getSkipMultiplier() {
883ace834feb02adabd61f628c4471147aea02d939cJohn Du        long currentTime = SystemClock.elapsedRealtime();
88419e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu        long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
88519e4c6abe87d6c213e802ecf7af6c1d00e1f4b65Zhihai Xu        return (int) Math.min(MAX_MULTIPLIER_VALUE, multi);
886ace834feb02adabd61f628c4471147aea02d939cJohn Du    }
887ace834feb02adabd61f628c4471147aea02d939cJohn Du
888c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private void sendTrackChangedRsp() {
889c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        byte[] track = new byte[TRACK_ID_SIZE];
890e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou
891e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou        /* If no track is currently selected, then return
892e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou           0xFFFFFFFFFFFFFFFF in the interim response */
893e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou        long trackNumberRsp = -1L;
894e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou
8952fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        if (isPlayingState(mCurrentPlayState)) {
896e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou            trackNumberRsp = mTrackNumber;
897e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou        }
898e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou
89979d176b0f3d6cb33c7e52be6641fd4808ba87e93Zhihai Xu        /* track is stored in big endian format */
900c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        for (int i = 0; i < TRACK_ID_SIZE; ++i) {
901e8e870c24930f9b687572dbb80614e5aad6fbdfaberyl hou            track[i] = (byte) (trackNumberRsp >> (56 - 8 * i));
902c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
903c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
904c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
905c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
906aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private long getPlayPosition() {
907eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        if (mCurrentPlayState == null)
908eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            return -1L;
909eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
910eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        if (mCurrentPlayState.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN)
911eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            return -1L;
912eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
913eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        if (isPlayingState(mCurrentPlayState)) {
914eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            return SystemClock.elapsedRealtime() - mLastStateUpdate + mCurrentPlayState.getPosition();
915aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu        }
916eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
91784d3f084d4d1a65eb7a481e5a7fe4a6ac827be7bMarie Janssen        return mCurrentPlayState.getPosition();
918aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    }
919aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu
9202fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private int convertPlayStateToPlayStatus(PlaybackState state) {
921c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        int playStatus = PLAYSTATUS_ERROR;
9222fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        switch (state.getState()) {
9232fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_PLAYING:
9242fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_BUFFERING:
925c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_PLAYING;
926c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
927c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
9282fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_STOPPED:
9292fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_NONE:
930c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_STOPPED;
931c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
932c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
9332fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_PAUSED:
934c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_PAUSED;
935c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
936c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
9372fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_FAST_FORWARDING:
9382fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_SKIPPING_TO_NEXT:
9392fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
940c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_FWD_SEEK;
941c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
942c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
9432fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_REWINDING:
9442fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
945c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_REV_SEEK;
946c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
947c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
9482fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen            case PlaybackState.STATE_ERROR:
949c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                playStatus = PLAYSTATUS_ERROR;
950c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu                break;
951c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
952c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        }
953c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu        return playStatus;
954c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    }
955c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
9562fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen    private boolean isPlayingState(PlaybackState state) {
9572fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen        return (state.getState() == PlaybackState.STATE_PLAYING) ||
9582fc493d0ea2b504df25d783a488dfadfe301329eMarie Janssen                (state.getState() == PlaybackState.STATE_BUFFERING);
959188f205b5f093850d4cc627917a21204be36c56aZhihai Xu    }
960188f205b5f093850d4cc627917a21204be36c56aZhihai Xu
96117675906064bb72fdcca75baa56cdf8bb8968d01John Du    /**
962eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen     * Sends a play position notification, or schedules one to be
963eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen     * sent later at an appropriate time. If |requested| is true,
964eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen     * does both because this was called in reponse to a request from the
965eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen     * TG.
966eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen     */
967eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen    private void sendPlayPosNotificationRsp(boolean requested) {
9687d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        if (!requested && mPlayPosChangedNT != NOTIFICATION_TYPE_INTERIM) {
9697d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: Not registered or requesting.");
9707d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            return;
9717d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        }
9727d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen
973eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        long playPositionMs = getPlayPosition();
974eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
975eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        // mNextPosMs is set to -1 when the previous position was invalid
976eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        // so this will be true if the new position is valid & old was invalid.
977eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        // mPlayPositionMs is set to -1 when the new position is invalid,
978eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        // and the old mPrevPosMs is >= 0 so this is true when the new is invalid
979eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        // and the old was valid.
9807d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: (" + requested + ") "
9817d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            + mPrevPosMs + " <=? " + playPositionMs + " <=? " + mNextPosMs);
9827d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        if (requested || ((mLastReportedPosition != playPositionMs) &&
9837d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen              (playPositionMs >= mNextPosMs) || (playPositionMs <= mPrevPosMs))) {
984eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            if (!requested) mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
9857d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPositionMs);
9867d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            mLastReportedPosition = playPositionMs;
987eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            if (playPositionMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
988eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen                mNextPosMs = playPositionMs + mPlaybackIntervalMs;
989eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen                mPrevPosMs = playPositionMs - mPlaybackIntervalMs;
990eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            } else {
991eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen                mNextPosMs = -1;
992eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen                mPrevPosMs = -1;
993eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            }
994eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        }
995eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
996eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
9977d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen        if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM && isPlayingState(mCurrentPlayState)) {
998eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
999eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            long delay = mPlaybackIntervalMs;
1000eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            if (mNextPosMs != -1) {
1001eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen                delay = mNextPosMs - (playPositionMs > 0 ? playPositionMs : 0);
1002eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            }
10037d4aa832cb9a8a9f21ce0fad5510ae82526c3d28Marie Janssen            if (DEBUG) Log.d(TAG, "PLAY_INTERVAL_TIMEOUT set for " + delay + "ms from now");
1004eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            mHandler.sendMessageDelayed(msg, delay);
1005eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        }
1006eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen    }
1007eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen
1008eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen    /**
100917675906064bb72fdcca75baa56cdf8bb8968d01John Du     * This is called from AudioService. It will return whether this device supports abs volume.
101017675906064bb72fdcca75baa56cdf8bb8968d01John Du     * NOT USED AT THE MOMENT.
101117675906064bb72fdcca75baa56cdf8bb8968d01John Du     */
101217675906064bb72fdcca75baa56cdf8bb8968d01John Du    public boolean isAbsoluteVolumeSupported() {
101317675906064bb72fdcca75baa56cdf8bb8968d01John Du        return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
101417675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
101517675906064bb72fdcca75baa56cdf8bb8968d01John Du
101617675906064bb72fdcca75baa56cdf8bb8968d01John Du    /**
101717675906064bb72fdcca75baa56cdf8bb8968d01John Du     * We get this call from AudioService. This will send a message to our handler object,
101817675906064bb72fdcca75baa56cdf8bb8968d01John Du     * requesting our handler to call setVolumeNative()
101917675906064bb72fdcca75baa56cdf8bb8968d01John Du     */
102017675906064bb72fdcca75baa56cdf8bb8968d01John Du    public void adjustVolume(int direction) {
102117675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
102217675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
102317675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
102417675906064bb72fdcca75baa56cdf8bb8968d01John Du
102517675906064bb72fdcca75baa56cdf8bb8968d01John Du    public void setAbsoluteVolume(int volume) {
102611798b011c962b602217b479130d413f3b30f19aLiejun Tao        if (volume == mLocalVolume) {
102711798b011c962b602217b479130d413f3b30f19aLiejun Tao            if (DEBUG) Log.v(TAG, "setAbsoluteVolume is setting same index, ignore "+volume);
102811798b011c962b602217b479130d413f3b30f19aLiejun Tao            return;
102911798b011c962b602217b479130d413f3b30f19aLiejun Tao        }
103011798b011c962b602217b479130d413f3b30f19aLiejun Tao
103117675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.removeMessages(MESSAGE_ADJUST_VOLUME);
103211798b011c962b602217b479130d413f3b30f19aLiejun Tao        Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, volume, 0);
103317675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
10345c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta    }
10355c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta
103617675906064bb72fdcca75baa56cdf8bb8968d01John Du    /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
103717675906064bb72fdcca75baa56cdf8bb8968d01John Du     * case when the volume is change locally on the carkit. This notification is not called when
103817675906064bb72fdcca75baa56cdf8bb8968d01John Du     * the volume is changed from the phone.
103917675906064bb72fdcca75baa56cdf8bb8968d01John Du     *
104017675906064bb72fdcca75baa56cdf8bb8968d01John Du     * This method will send a message to our handler to change the local stored volume and notify
104117675906064bb72fdcca75baa56cdf8bb8968d01John Du     * AudioService to update the UI
104217675906064bb72fdcca75baa56cdf8bb8968d01John Du     */
104317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private void volumeChangeCallback(int volume, int ctype) {
104417675906064bb72fdcca75baa56cdf8bb8968d01John Du        Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype);
104517675906064bb72fdcca75baa56cdf8bb8968d01John Du        mHandler.sendMessage(msg);
104617675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
104717675906064bb72fdcca75baa56cdf8bb8968d01John Du
10482e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie    private void notifyVolumeChanged(int volume) {
10492e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
10502e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie                      AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
105117675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
105217675906064bb72fdcca75baa56cdf8bb8968d01John Du
105317675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int convertToAudioStreamVolume(int volume) {
105417675906064bb72fdcca75baa56cdf8bb8968d01John Du        // Rescale volume to match AudioSystem's volume
105511798b011c962b602217b479130d413f3b30f19aLiejun Tao        return (int) Math.floor((double) volume*mAudioStreamMax/AVRCP_MAX_VOL);
105617675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
105717675906064bb72fdcca75baa56cdf8bb8968d01John Du
105817675906064bb72fdcca75baa56cdf8bb8968d01John Du    private int convertToAvrcpVolume(int volume) {
10592e90040ffc6f1c4cd5187338c9f708df4d380aeeMatthew Xie        return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);
106017675906064bb72fdcca75baa56cdf8bb8968d01John Du    }
106117675906064bb72fdcca75baa56cdf8bb8968d01John Du
106211798b011c962b602217b479130d413f3b30f19aLiejun Tao    private void blackListCurrentDevice() {
106311798b011c962b602217b479130d413f3b30f19aLiejun Tao        mFeatures &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
106411798b011c962b602217b479130d413f3b30f19aLiejun Tao        mAudioManager.avrcpSupportsAbsoluteVolume(mAddress, isAbsoluteVolumeSupported());
106511798b011c962b602217b479130d413f3b30f19aLiejun Tao
106611798b011c962b602217b479130d413f3b30f19aLiejun Tao        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
106711798b011c962b602217b479130d413f3b30f19aLiejun Tao                Context.MODE_PRIVATE);
106811798b011c962b602217b479130d413f3b30f19aLiejun Tao        SharedPreferences.Editor editor = pref.edit();
106911798b011c962b602217b479130d413f3b30f19aLiejun Tao        editor.putBoolean(mAddress, true);
107011798b011c962b602217b479130d413f3b30f19aLiejun Tao        editor.commit();
107111798b011c962b602217b479130d413f3b30f19aLiejun Tao    }
107211798b011c962b602217b479130d413f3b30f19aLiejun Tao
107311798b011c962b602217b479130d413f3b30f19aLiejun Tao    private int modifyRcFeatureFromBlacklist(int feature, String address) {
107411798b011c962b602217b479130d413f3b30f19aLiejun Tao        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
107511798b011c962b602217b479130d413f3b30f19aLiejun Tao                Context.MODE_PRIVATE);
107611798b011c962b602217b479130d413f3b30f19aLiejun Tao        if (!pref.contains(address)) {
107711798b011c962b602217b479130d413f3b30f19aLiejun Tao            return feature;
107811798b011c962b602217b479130d413f3b30f19aLiejun Tao        }
107911798b011c962b602217b479130d413f3b30f19aLiejun Tao        if (pref.getBoolean(address, false)) {
108011798b011c962b602217b479130d413f3b30f19aLiejun Tao            feature &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
108111798b011c962b602217b479130d413f3b30f19aLiejun Tao        }
108211798b011c962b602217b479130d413f3b30f19aLiejun Tao        return feature;
108311798b011c962b602217b479130d413f3b30f19aLiejun Tao    }
108411798b011c962b602217b479130d413f3b30f19aLiejun Tao
108511798b011c962b602217b479130d413f3b30f19aLiejun Tao    public void resetBlackList(String address) {
108611798b011c962b602217b479130d413f3b30f19aLiejun Tao        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
108711798b011c962b602217b479130d413f3b30f19aLiejun Tao                Context.MODE_PRIVATE);
108811798b011c962b602217b479130d413f3b30f19aLiejun Tao        SharedPreferences.Editor editor = pref.edit();
108911798b011c962b602217b479130d413f3b30f19aLiejun Tao        editor.remove(address);
109011798b011c962b602217b479130d413f3b30f19aLiejun Tao        editor.commit();
109111798b011c962b602217b479130d413f3b30f19aLiejun Tao    }
109211798b011c962b602217b479130d413f3b30f19aLiejun Tao
1093188f205b5f093850d4cc627917a21204be36c56aZhihai Xu    /**
1094188f205b5f093850d4cc627917a21204be36c56aZhihai Xu     * This is called from A2dpStateMachine to set A2dp audio state.
1095188f205b5f093850d4cc627917a21204be36c56aZhihai Xu     */
1096188f205b5f093850d4cc627917a21204be36c56aZhihai Xu    public void setA2dpAudioState(int state) {
1097188f205b5f093850d4cc627917a21204be36c56aZhihai Xu        Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0);
1098188f205b5f093850d4cc627917a21204be36c56aZhihai Xu        mHandler.sendMessage(msg);
1099188f205b5f093850d4cc627917a21204be36c56aZhihai Xu    }
1100188f205b5f093850d4cc627917a21204be36c56aZhihai Xu
1101838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    public void dump(StringBuilder sb) {
1102838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        sb.append("AVRCP:\n");
11030fcd081cccbf624a3a886fe4eb68adc3498e695cMarie Janssen        ProfileService.println(sb, "mMediaAttributes: " + mMediaAttributes);
1104838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
1105838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
1106eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        ProfileService.println(sb, "mLastStateUpdate: " + mLastStateUpdate);
1107838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
1108838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
1109838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mTrackNumber: " + mTrackNumber);
1110838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
1111838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
1112838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
1113838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mNextPosMs: " + mNextPosMs);
1114838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs);
1115838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mSkipStartTime: " + mSkipStartTime);
1116838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mFeatures: " + mFeatures);
111711798b011c962b602217b479130d413f3b30f19aLiejun Tao        ProfileService.println(sb, "mRemoteVolume: " + mRemoteVolume);
111811798b011c962b602217b479130d413f3b30f19aLiejun Tao        ProfileService.println(sb, "mLastRemoteVolume: " + mLastRemoteVolume);
1119838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mLastDirection: " + mLastDirection);
1120838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mVolumeStep: " + mVolumeStep);
1121838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax);
112211798b011c962b602217b479130d413f3b30f19aLiejun Tao        ProfileService.println(sb, "mVolCmdAdjustInProgress: " + mVolCmdAdjustInProgress);
112311798b011c962b602217b479130d413f3b30f19aLiejun Tao        ProfileService.println(sb, "mVolCmdSetInProgress: " + mVolCmdSetInProgress);
1124838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes);
1125838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood        ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
112611798b011c962b602217b479130d413f3b30f19aLiejun Tao        ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString());
1127eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen        if (mMediaController != null)
1128eb9e6ef0ecde4929fa2df1aa4cc8954d88d3b452Marie Janssen            ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName());
1129838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood    }
1130838949d46a4cc054985a8cfd682004f8dd6d3bbbMike Lockwood
1131c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // Do not modify without updating the HAL bt_rc.h files.
1132c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
1133c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_play_status_t enum of bt_rc.h
1134c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_STOPPED = 0;
1135c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_PLAYING = 1;
1136c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_PAUSED = 2;
1137c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_FWD_SEEK = 3;
1138c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_REV_SEEK = 4;
1139c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int PLAYSTATUS_ERROR = 255;
1140c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
1141c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_media_attr_t enum of bt_rc.h
1142c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_TITLE = 1;
1143c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_ARTIST = 2;
1144c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_ALBUM = 3;
1145c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_TRACK_NUM = 4;
1146c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_NUM_TRACKS = 5;
1147c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_GENRE = 6;
1148c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int MEDIA_ATTR_PLAYING_TIME = 7;
1149c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
1150c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_event_id_t enum of bt_rc.h
1151c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_PLAY_STATUS_CHANGED = 1;
1152c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_TRACK_CHANGED = 2;
1153c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_TRACK_REACHED_END = 3;
1154c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_TRACK_REACHED_START = 4;
1155c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_PLAY_POS_CHANGED = 5;
1156c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_BATT_STATUS_CHANGED = 6;
1157c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_SYSTEM_STATUS_CHANGED = 7;
1158c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int EVT_APP_SETTINGS_CHANGED = 8;
1159c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
1160c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with btrc_notification_type_t enum of bt_rc.h
1161c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int NOTIFICATION_TYPE_INTERIM = 0;
1162c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int NOTIFICATION_TYPE_CHANGED = 1;
1163c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
1164c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    // match up with BTRC_UID_SIZE of bt_rc.h
1165c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    final static int TRACK_ID_SIZE = 8;
1166c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu
1167c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native static void classInitNative();
1168c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native void initNative();
1169c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native void cleanupNative();
1170c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
1171c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
1172c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
1173c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu    private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
1174aa1ffd5c6bd4f016c6ed452b3551e65872eaea8eZhihai Xu    private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
117517675906064bb72fdcca75baa56cdf8bb8968d01John Du    private native boolean setVolumeNative(int volume);
11765c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta    private native boolean sendPassThroughCommandNative(int keyCode, int keyState);
11775c86abdc7875a8fae28c57e3e36a0a8eaae5cb04Hemant Gupta
1178c1c259c0ace7195240f1443c805995bfe8692a72Zhihai Xu}
1179