Avrcp.java revision e2c4671a8c5d01f053b5b8e0c728d69d561abdc2
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.bluetooth.avrcp;
18
19import java.util.Timer;
20import java.util.TimerTask;
21
22import android.bluetooth.BluetoothA2dp;
23import android.bluetooth.BluetoothAvrcp;
24import android.content.Context;
25import android.content.Intent;
26import android.content.res.Resources;
27import android.content.SharedPreferences;
28import android.graphics.Bitmap;
29import android.media.AudioManager;
30import android.media.MediaMetadataRetriever;
31import android.media.RemoteControlClient;
32import android.media.RemoteController;
33import android.media.RemoteController.MetadataEditor;
34import android.os.Bundle;
35import android.os.Handler;
36import android.os.HandlerThread;
37import android.os.Looper;
38import android.os.Message;
39import android.os.ParcelUuid;
40import android.os.PowerManager;
41import android.os.PowerManager.WakeLock;
42import android.os.RemoteException;
43import android.os.ServiceManager;
44import android.os.SystemClock;
45import android.util.Log;
46import android.view.KeyEvent;
47
48import com.android.bluetooth.R;
49import com.android.bluetooth.btservice.AdapterService;
50import com.android.bluetooth.btservice.ProfileService;
51import com.android.bluetooth.Utils;
52import com.android.internal.util.IState;
53import com.android.internal.util.State;
54import com.android.internal.util.StateMachine;
55
56import java.lang.ref.WeakReference;
57import java.util.ArrayList;
58import java.util.HashMap;
59import java.util.List;
60import java.util.Set;
61/**
62 * support Bluetooth AVRCP profile.
63 * support metadata, play status and event notification
64 */
65public final class Avrcp {
66    private static final boolean DEBUG = false;
67    private static final String TAG = "Avrcp";
68    private static final String ABSOLUTE_VOLUME_BLACKLIST = "absolute_volume_blacklist";
69
70    private Context mContext;
71    private final AudioManager mAudioManager;
72    private AvrcpMessageHandler mHandler;
73    private RemoteController mRemoteController;
74    private RemoteControllerWeak mRemoteControllerCb;
75    private Metadata mMetadata;
76    private int mTransportControlFlags;
77    private int mCurrentPlayState;
78    private int mPlayStatusChangedNT;
79    private int mTrackChangedNT;
80    private long mTrackNumber;
81    private long mCurrentPosMs;
82    private long mPlayStartTimeMs;
83    private long mSongLengthMs;
84    private long mPlaybackIntervalMs;
85    private int mPlayPosChangedNT;
86    private long mNextPosMs;
87    private long mPrevPosMs;
88    private long mSkipStartTime;
89    private int mFeatures;
90    private int mRemoteVolume;
91    private int mLastRemoteVolume;
92    private int mInitialRemoteVolume;
93
94    /* Local volume in audio index 0-15 */
95    private int mLocalVolume;
96    private int mLastLocalVolume;
97    private int mAbsVolThreshold;
98
99    private String mAddress;
100    private HashMap<Integer, Integer> mVolumeMapping;
101
102    private int mLastDirection;
103    private final int mVolumeStep;
104    private final int mAudioStreamMax;
105    private boolean mVolCmdAdjustInProgress;
106    private boolean mVolCmdSetInProgress;
107    private int mAbsVolRetryTimes;
108    private int mSkipAmount;
109
110    /* BTRC features */
111    public static final int BTRC_FEAT_METADATA = 0x01;
112    public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
113    public static final int BTRC_FEAT_BROWSE = 0x04;
114
115    /* AVRC response codes, from avrc_defs */
116    private static final int AVRC_RSP_NOT_IMPL = 8;
117    private static final int AVRC_RSP_ACCEPT = 9;
118    private static final int AVRC_RSP_REJ = 10;
119    private static final int AVRC_RSP_IN_TRANS = 11;
120    private static final int AVRC_RSP_IMPL_STBL = 12;
121    private static final int AVRC_RSP_CHANGED = 13;
122    private static final int AVRC_RSP_INTERIM = 15;
123
124    private static final int MESSAGE_GET_RC_FEATURES = 1;
125    private static final int MESSAGE_GET_PLAY_STATUS = 2;
126    private static final int MESSAGE_GET_ELEM_ATTRS = 3;
127    private static final int MESSAGE_REGISTER_NOTIFICATION = 4;
128    private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5;
129    private static final int MESSAGE_VOLUME_CHANGED = 6;
130    private static final int MESSAGE_ADJUST_VOLUME = 7;
131    private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8;
132    private static final int MESSAGE_ABS_VOL_TIMEOUT = 9;
133    private static final int MESSAGE_FAST_FORWARD = 10;
134    private static final int MESSAGE_REWIND = 11;
135    private static final int MESSAGE_CHANGE_PLAY_POS = 12;
136    private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13;
137    private static final int MSG_UPDATE_STATE = 100;
138    private static final int MSG_SET_METADATA = 101;
139    private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
140    private static final int MSG_SET_GENERATION_ID = 104;
141
142    private static final int BUTTON_TIMEOUT_TIME = 2000;
143    private static final int BASE_SKIP_AMOUNT = 2000;
144    private static final int KEY_STATE_PRESS = 1;
145    private static final int KEY_STATE_RELEASE = 0;
146    private static final int SKIP_PERIOD = 400;
147    private static final int SKIP_DOUBLE_INTERVAL = 3000;
148    private static final long MAX_MULTIPLIER_VALUE = 128L;
149    private static final int CMD_TIMEOUT_DELAY = 2000;
150    private static final int MAX_ERROR_RETRY_TIMES = 3;
151    private static final int AVRCP_MAX_VOL = 127;
152    private static final int AVRCP_BASE_VOLUME_STEP = 1;
153
154    static {
155        classInitNative();
156    }
157
158    private Avrcp(Context context) {
159        mMetadata = new Metadata();
160        mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback
161        mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
162        mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
163        mTrackNumber = -1L;
164        mCurrentPosMs = RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN;
165        mPlayStartTimeMs = -1L;
166        mSongLengthMs = 0L;
167        mPlaybackIntervalMs = 0L;
168        mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
169        mFeatures = 0;
170        mRemoteVolume = -1;
171        mInitialRemoteVolume = -1;
172        mLastRemoteVolume = -1;
173        mLastDirection = 0;
174        mVolCmdAdjustInProgress = false;
175        mVolCmdSetInProgress = false;
176        mAbsVolRetryTimes = 0;
177        mLocalVolume = -1;
178        mLastLocalVolume = -1;
179        mAbsVolThreshold = 0;
180        mVolumeMapping = new HashMap<Integer, Integer>();
181
182        mContext = context;
183
184        initNative();
185
186        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
187        mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
188        mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax);
189        Resources resources = context.getResources();
190        if (resources != null) {
191            mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
192        }
193    }
194
195    private void start() {
196        HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
197        thread.start();
198        Looper looper = thread.getLooper();
199        mHandler = new AvrcpMessageHandler(looper);
200        mRemoteControllerCb = new RemoteControllerWeak(mHandler);
201        mRemoteController = new RemoteController(mContext, mRemoteControllerCb);
202        mAudioManager.registerRemoteController(mRemoteController);
203        mRemoteController.setSynchronizationMode(RemoteController.POSITION_SYNCHRONIZATION_CHECK);
204    }
205
206    public static Avrcp make(Context context) {
207        if (DEBUG) Log.v(TAG, "make");
208        Avrcp ar = new Avrcp(context);
209        ar.start();
210        return ar;
211    }
212
213    public void doQuit() {
214        mHandler.removeCallbacksAndMessages(null);
215        Looper looper = mHandler.getLooper();
216        if (looper != null) {
217            looper.quit();
218        }
219        mAudioManager.unregisterRemoteController(mRemoteController);
220    }
221
222    public void cleanup() {
223        cleanupNative();
224        if (mVolumeMapping != null)
225            mVolumeMapping.clear();
226    }
227
228    private static class RemoteControllerWeak implements RemoteController.OnClientUpdateListener {
229        private final WeakReference<Handler> mLocalHandler;
230
231        public RemoteControllerWeak(Handler handler) {
232            mLocalHandler = new WeakReference<Handler>(handler);
233        }
234
235        @Override
236        public void onClientChange(boolean clearing) {
237            Handler handler = mLocalHandler.get();
238            if (handler != null) {
239                handler.obtainMessage(MSG_SET_GENERATION_ID,
240                        0, (clearing ? 1 : 0), null).sendToTarget();
241            }
242        }
243
244        @Override
245        public void onClientPlaybackStateUpdate(int state) {
246            // Should never be called with the existing code, but just in case
247            if (DEBUG) Log.v(TAG, "RemoteControlDisplayer: Update playbackState state=" + state + " position=null");
248            Handler handler = mLocalHandler.get();
249            if (handler != null) {
250                handler.obtainMessage(MSG_UPDATE_STATE, 0, state,
251                        new Long(RemoteControlClient.PLAYBACK_POSITION_INVALID)).sendToTarget();
252            }
253        }
254
255        @Override
256        public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
257                long currentPosMs, float speed) {
258            if (DEBUG) Log.v(TAG, "RemoteControlDisplayer: Update playbackState state=" + state + " position=" + currentPosMs);
259            Handler handler = mLocalHandler.get();
260            if (handler != null) {
261                handler.obtainMessage(MSG_UPDATE_STATE, 0, state,
262                        new Long(currentPosMs)).sendToTarget();
263            }
264        }
265
266        @Override
267        public void onClientTransportControlUpdate(int transportControlFlags) {
268            Handler handler = mLocalHandler.get();
269            if (handler != null) {
270                handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, 0, transportControlFlags)
271                        .sendToTarget();
272            }
273        }
274
275        @Override
276        public void onClientMetadataUpdate(MetadataEditor metadataEditor) {
277            Log.v(TAG, "RemoteControlDisplayer: Update Metadata");
278            Handler handler = mLocalHandler.get();
279            if (handler != null) {
280                handler.obtainMessage(MSG_SET_METADATA, 0, 0, metadataEditor).sendToTarget();
281            }
282        }
283    }
284
285    /** Handles Avrcp messages. */
286    private final class AvrcpMessageHandler extends Handler {
287        private AvrcpMessageHandler(Looper looper) {
288            super(looper);
289        }
290
291        @Override
292        public void handleMessage(Message msg) {
293            switch (msg.what) {
294            case MSG_UPDATE_STATE:
295                updatePlayPauseState(msg.arg2, ((Long) msg.obj).longValue());
296                break;
297
298            case MSG_SET_METADATA:
299                updateMetadata((MetadataEditor) msg.obj);
300                break;
301
302            case MSG_SET_TRANSPORT_CONTROLS:
303                updateTransportControls(msg.arg2);
304                break;
305
306            case MSG_SET_GENERATION_ID:
307                Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2);
308                break;
309
310            case MESSAGE_GET_RC_FEATURES:
311                String address = (String) msg.obj;
312                if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+
313                                                             ", features="+msg.arg1);
314                mFeatures = msg.arg1;
315                mFeatures = modifyRcFeatureFromBlacklist(mFeatures, address);
316                mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported());
317                mLastLocalVolume = -1;
318                mRemoteVolume = -1;
319                mLocalVolume = -1;
320                mInitialRemoteVolume = -1;
321                mAddress = address;
322                if (mVolumeMapping != null)
323                    mVolumeMapping.clear();
324                break;
325
326            case MESSAGE_GET_PLAY_STATUS:
327                if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
328                getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState),
329                                       (int)mSongLengthMs, (int)getPlayPosition());
330                break;
331
332            case MESSAGE_GET_ELEM_ATTRS:
333                String[] textArray;
334                int[] attrIds;
335                byte numAttr = (byte) msg.arg1;
336                ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj;
337                Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
338                attrIds = new int[numAttr];
339                textArray = new String[numAttr];
340                for (int i = 0; i < numAttr; ++i) {
341                    attrIds[i] = attrList.get(i).intValue();
342                    textArray[i] = getAttributeString(attrIds[i]);
343                }
344                getElementAttrRspNative(numAttr, attrIds, textArray);
345                break;
346
347            case MESSAGE_REGISTER_NOTIFICATION:
348                if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
349                                      " param=" + msg.arg2);
350                processRegisterNotification(msg.arg1, msg.arg2);
351                break;
352
353            case MESSAGE_PLAY_INTERVAL_TIMEOUT:
354                if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
355                mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
356                registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)getPlayPosition());
357                break;
358
359            case MESSAGE_VOLUME_CHANGED:
360                if (!isAbsoluteVolumeSupported()) {
361                    if (DEBUG) Log.v(TAG, "ignore MESSAGE_VOLUME_CHANGED");
362                    break;
363                }
364
365                if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f)
366                                                        + " ctype=" + msg.arg2);
367
368
369                boolean volAdj = false;
370                if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) {
371                    if (mVolCmdAdjustInProgress == false && mVolCmdSetInProgress == false) {
372                        Log.e(TAG, "Unsolicited response, ignored");
373                        break;
374                    }
375                    removeMessages(MESSAGE_ABS_VOL_TIMEOUT);
376
377                    volAdj = mVolCmdAdjustInProgress;
378                    mVolCmdAdjustInProgress = false;
379                    mVolCmdSetInProgress = false;
380                    mAbsVolRetryTimes = 0;
381                }
382
383                byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD
384                // convert remote volume to local volume
385                int volIndex = convertToAudioStreamVolume(absVol);
386                if (mInitialRemoteVolume == -1) {
387                    mInitialRemoteVolume = absVol;
388                    if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax && volIndex > mAbsVolThreshold) {
389                        if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" + mAbsVolThreshold);
390                        Message msg1 = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, mAbsVolThreshold , 0);
391                        mHandler.sendMessage(msg1);
392                        mRemoteVolume = absVol;
393                        mLocalVolume = volIndex;
394                        break;
395                    }
396                }
397
398                if (mLocalVolume != volIndex &&
399                                                   (msg.arg2 == AVRC_RSP_ACCEPT ||
400                                                    msg.arg2 == AVRC_RSP_CHANGED ||
401                                                    msg.arg2 == AVRC_RSP_INTERIM)) {
402                    /* If the volume has successfully changed */
403                    mLocalVolume = volIndex;
404                    if (mLastLocalVolume != -1 && msg.arg2 == AVRC_RSP_ACCEPT) {
405                        if (mLastLocalVolume != volIndex) {
406                            /* remote volume changed more than requested due to
407                             * local and remote has different volume steps */
408                            if (DEBUG) Log.d(TAG, "Remote returned volume does not match desired volume "
409                                + mLastLocalVolume + " vs "
410                                + volIndex);
411                            mLastLocalVolume = mLocalVolume;
412                        }
413                    }
414                    // remember the remote volume value, as it's the one supported by remote
415                    if (volAdj) {
416                        synchronized (mVolumeMapping) {
417                            mVolumeMapping.put(volIndex, (int)absVol);
418                            if (DEBUG) Log.v(TAG, "remember volume mapping " +volIndex+ "-"+absVol);
419                        }
420                    }
421
422                    notifyVolumeChanged(mLocalVolume);
423                    mRemoteVolume = absVol;
424                    long pecentVolChanged = ((long)absVol * 100) / 0x7f;
425                    Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%");
426                } else if (msg.arg2 == AVRC_RSP_REJ) {
427                    Log.e(TAG, "setAbsoluteVolume call rejected");
428                } else if (volAdj && mLastRemoteVolume > 0 && mLastRemoteVolume < AVRCP_MAX_VOL &&
429                           mLocalVolume == volIndex &&
430                                                   (msg.arg2 == AVRC_RSP_ACCEPT )) {
431                    /* oops, the volume is still same, remote does not like the value
432                     * retry a volume one step up/down */
433                    if (DEBUG) Log.d(TAG, "Remote device didn't tune volume, let's try one more step.");
434                    int retry_volume = Math.min(AVRCP_MAX_VOL,
435                            Math.max(0, mLastRemoteVolume + mLastDirection));
436                    if (setVolumeNative(retry_volume)) {
437                        mLastRemoteVolume = retry_volume;
438                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
439                                           CMD_TIMEOUT_DELAY);
440                        mVolCmdAdjustInProgress = true;
441                    }
442                }
443                break;
444
445            case MESSAGE_ADJUST_VOLUME:
446                if (!isAbsoluteVolumeSupported()) {
447                    if (DEBUG) Log.v(TAG, "ignore MESSAGE_ADJUST_VOLUME");
448                    break;
449                }
450
451                if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1);
452
453                if (mVolCmdAdjustInProgress || mVolCmdSetInProgress) {
454                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
455                    break;
456                }
457
458                // Remote device didn't set initial volume. Let's black list it
459                if (mInitialRemoteVolume == -1) {
460                    Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
461                    blackListCurrentDevice();
462                    break;
463                }
464
465                // Wait on verification on volume from device, before changing the volume.
466                if (mRemoteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) {
467                    int setVol = -1;
468                    int targetVolIndex = -1;
469                    if (mLocalVolume == 0 && msg.arg1 == -1) {
470                        if (DEBUG) Log.w(TAG, "No need to Vol down from 0.");
471                        break;
472                    }
473                    if (mLocalVolume == mAudioStreamMax && msg.arg1 == 1) {
474                        if (DEBUG) Log.w(TAG, "No need to Vol up from max.");
475                        break;
476                    }
477
478                    targetVolIndex = mLocalVolume + msg.arg1;
479                    if (DEBUG) Log.d(TAG, "Adjusting volume to  " + targetVolIndex);
480
481                    Integer i;
482                    synchronized (mVolumeMapping) {
483                        i = mVolumeMapping.get(targetVolIndex);
484                    }
485
486                    if (i != null) {
487                        /* if we already know this volume mapping, use it */
488                        setVol = i.byteValue();
489                        if (setVol == mRemoteVolume) {
490                            if (DEBUG) Log.d(TAG, "got same volume from mapping for " + targetVolIndex + ", ignore.");
491                            setVol = -1;
492                        }
493                        if (DEBUG) Log.d(TAG, "set volume from mapping " + targetVolIndex + "-" + setVol);
494                    }
495
496                    if (setVol == -1) {
497                        /* otherwise use phone steps */
498                        setVol = Math.min(AVRCP_MAX_VOL,
499                                 convertToAvrcpVolume(Math.max(0, targetVolIndex)));
500                        if (DEBUG) Log.d(TAG, "set volume from local volume "+ targetVolIndex+"-"+ setVol);
501                    }
502
503                    if (setVolumeNative(setVol)) {
504                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
505                                           CMD_TIMEOUT_DELAY);
506                        mVolCmdAdjustInProgress = true;
507                        mLastDirection = msg.arg1;
508                        mLastRemoteVolume = setVol;
509                        mLastLocalVolume = targetVolIndex;
510                    } else {
511                         if (DEBUG) Log.d(TAG, "setVolumeNative failed");
512                    }
513                } else {
514                    Log.e(TAG, "Unknown direction in MESSAGE_ADJUST_VOLUME");
515                }
516                break;
517
518            case MESSAGE_SET_ABSOLUTE_VOLUME:
519                if (!isAbsoluteVolumeSupported()) {
520                    if (DEBUG) Log.v(TAG, "ignore MESSAGE_SET_ABSOLUTE_VOLUME");
521                    break;
522                }
523
524                if (DEBUG) Log.v(TAG, "MESSAGE_SET_ABSOLUTE_VOLUME");
525
526                if (mVolCmdSetInProgress || mVolCmdAdjustInProgress) {
527                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
528                    break;
529                }
530
531                // Remote device didn't set initial volume. Let's black list it
532                if (mInitialRemoteVolume == -1) {
533                    if (DEBUG) Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
534                    blackListCurrentDevice();
535                    break;
536                }
537
538                int avrcpVolume = convertToAvrcpVolume(msg.arg1);
539                avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
540                if (DEBUG) Log.d(TAG, "Setting volume to " + msg.arg1+"-"+avrcpVolume);
541                if (setVolumeNative(avrcpVolume)) {
542                    sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
543                    mVolCmdSetInProgress = true;
544                    mLastRemoteVolume = avrcpVolume;
545                    mLastLocalVolume = msg.arg1;
546                } else {
547                     if (DEBUG) Log.d(TAG, "setVolumeNative failed");
548                }
549                break;
550
551            case MESSAGE_ABS_VOL_TIMEOUT:
552                if (DEBUG) Log.v(TAG, "MESSAGE_ABS_VOL_TIMEOUT: Volume change cmd timed out.");
553                mVolCmdAdjustInProgress = false;
554                mVolCmdSetInProgress = false;
555                if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
556                    mAbsVolRetryTimes = 0;
557                } else {
558                    mAbsVolRetryTimes += 1;
559                    if (setVolumeNative(mLastRemoteVolume)) {
560                        sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
561                                           CMD_TIMEOUT_DELAY);
562                        mVolCmdSetInProgress = true;
563                    }
564                }
565                break;
566
567            case MESSAGE_FAST_FORWARD:
568            case MESSAGE_REWIND:
569                if(msg.what == MESSAGE_FAST_FORWARD) {
570                    if((mTransportControlFlags &
571                        RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD) != 0) {
572                    int keyState = msg.arg1 == KEY_STATE_PRESS ?
573                        KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
574                    KeyEvent keyEvent =
575                        new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
576                    mRemoteController.sendMediaKeyEvent(keyEvent);
577                    break;
578                    }
579                } else if((mTransportControlFlags &
580                        RemoteControlClient.FLAG_KEY_MEDIA_REWIND) != 0) {
581                    int keyState = msg.arg1 == KEY_STATE_PRESS ?
582                        KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
583                    KeyEvent keyEvent =
584                        new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND);
585                    mRemoteController.sendMediaKeyEvent(keyEvent);
586                    break;
587                }
588
589                int skipAmount;
590                if (msg.what == MESSAGE_FAST_FORWARD) {
591                    if (DEBUG) Log.v(TAG, "MESSAGE_FAST_FORWARD");
592                    removeMessages(MESSAGE_FAST_FORWARD);
593                    skipAmount = BASE_SKIP_AMOUNT;
594                } else {
595                    if (DEBUG) Log.v(TAG, "MESSAGE_REWIND");
596                    removeMessages(MESSAGE_REWIND);
597                    skipAmount = -BASE_SKIP_AMOUNT;
598                }
599
600                if (hasMessages(MESSAGE_CHANGE_PLAY_POS) &&
601                        (skipAmount != mSkipAmount)) {
602                    Log.w(TAG, "missing release button event:" + mSkipAmount);
603                }
604
605                if ((!hasMessages(MESSAGE_CHANGE_PLAY_POS)) ||
606                        (skipAmount != mSkipAmount)) {
607                    mSkipStartTime = SystemClock.elapsedRealtime();
608                }
609
610                removeMessages(MESSAGE_CHANGE_PLAY_POS);
611                if (msg.arg1 == KEY_STATE_PRESS) {
612                    mSkipAmount = skipAmount;
613                    changePositionBy(mSkipAmount * getSkipMultiplier());
614                    Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
615                    posMsg.arg1 = 1;
616                    sendMessageDelayed(posMsg, SKIP_PERIOD);
617                }
618
619                break;
620
621            case MESSAGE_CHANGE_PLAY_POS:
622                if (DEBUG) Log.v(TAG, "MESSAGE_CHANGE_PLAY_POS:" + msg.arg1);
623                changePositionBy(mSkipAmount * getSkipMultiplier());
624                if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) {
625                    Message posMsg = obtainMessage(MESSAGE_CHANGE_PLAY_POS);
626                    posMsg.arg1 = msg.arg1 + 1;
627                    sendMessageDelayed(posMsg, SKIP_PERIOD);
628                }
629                break;
630
631            case MESSAGE_SET_A2DP_AUDIO_STATE:
632                if (DEBUG) Log.v(TAG, "MESSAGE_SET_A2DP_AUDIO_STATE:" + msg.arg1);
633                updateA2dpAudioState(msg.arg1);
634                break;
635            }
636        }
637    }
638
639    private void updateA2dpAudioState(int state) {
640        boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
641        if (isPlaying != isPlayingState(mCurrentPlayState)) {
642            /* if a2dp is streaming, check to make sure music is active */
643            if ( (isPlaying) && !mAudioManager.isMusicActive())
644                return;
645            updatePlayPauseState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING :
646                                 RemoteControlClient.PLAYSTATE_PAUSED,
647                                 RemoteControlClient.PLAYBACK_POSITION_INVALID);
648        }
649    }
650
651    private void updatePlayPauseState(int state, long currentPosMs) {
652        if (DEBUG) Log.v(TAG,
653                "updatePlayPauseState, old=" + mCurrentPlayState + ", state=" + state);
654        boolean oldPosValid = (mCurrentPosMs !=
655                               RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
656        int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
657        int newPlayStatus = convertPlayStateToPlayStatus(state);
658
659        if ((mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) &&
660            (mCurrentPlayState != state) && oldPosValid) {
661            mCurrentPosMs = getPlayPosition();
662        }
663
664        if (currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) {
665            mCurrentPosMs = currentPosMs;
666        }
667        if ((state == RemoteControlClient.PLAYSTATE_PLAYING) &&
668            ((currentPosMs != RemoteControlClient.PLAYBACK_POSITION_INVALID) ||
669            (mCurrentPlayState != RemoteControlClient.PLAYSTATE_PLAYING))) {
670            mPlayStartTimeMs = SystemClock.elapsedRealtime();
671        }
672        mCurrentPlayState = state;
673
674        boolean newPosValid = (mCurrentPosMs !=
675                               RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN);
676        long playPosition = getPlayPosition();
677        mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
678        /* need send play position changed notification when play status is changed */
679        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) &&
680            ((oldPlayStatus != newPlayStatus) || (oldPosValid != newPosValid) ||
681             (newPosValid && ((playPosition >= mNextPosMs) || (playPosition <= mPrevPosMs))))) {
682            mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
683            registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)playPosition);
684        }
685        if ((mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) && newPosValid &&
686            (state == RemoteControlClient.PLAYSTATE_PLAYING)) {
687            Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
688            mHandler.sendMessageDelayed(msg, mNextPosMs - playPosition);
689        }
690
691        if ((mPlayStatusChangedNT == NOTIFICATION_TYPE_INTERIM) && (oldPlayStatus != newPlayStatus)) {
692            mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
693            registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
694        }
695    }
696
697    private void updateTransportControls(int transportControlFlags) {
698        mTransportControlFlags = transportControlFlags;
699    }
700
701    class Metadata {
702        private String artist;
703        private String trackTitle;
704        private String albumTitle;
705
706        public Metadata() {
707            artist = null;
708            trackTitle = null;
709            albumTitle = null;
710        }
711
712        public String toString() {
713            return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" +
714                   albumTitle + "]";
715        }
716    }
717
718    private void updateMetadata(MetadataEditor data) {
719        String oldMetadata = mMetadata.toString();
720        mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null);
721        mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null);
722        mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, null);
723        if (!oldMetadata.equals(mMetadata.toString())) {
724            Log.v(TAG, "Metadata Changed to " + mMetadata.toString());
725            mTrackNumber++;
726            if (mTrackChangedNT == NOTIFICATION_TYPE_INTERIM) {
727                mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
728                sendTrackChangedRsp();
729            }
730
731            if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
732                if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
733                    mPlayStartTimeMs = SystemClock.elapsedRealtime();
734                }
735            }
736            /* need send play position changed notification when track is changed */
737            if (mPlayPosChangedNT == NOTIFICATION_TYPE_INTERIM) {
738                mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
739                registerNotificationRspPlayPosNative(mPlayPosChangedNT,
740                                                     (int)getPlayPosition());
741                mHandler.removeMessages(MESSAGE_PLAY_INTERVAL_TIMEOUT);
742            }
743        } else {
744          Log.v(TAG, "Metadata updated but no change!");
745        }
746
747        mSongLengthMs = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION,
748                RemoteControlClient.PLAYBACK_POSITION_INVALID);
749    }
750
751    private void getRcFeatures(byte[] address, int features) {
752        Message msg = mHandler.obtainMessage(MESSAGE_GET_RC_FEATURES, features, 0,
753                                             Utils.getAddressStringFromByte(address));
754        mHandler.sendMessage(msg);
755    }
756
757    private void getPlayStatus() {
758        Message msg = mHandler.obtainMessage(MESSAGE_GET_PLAY_STATUS);
759        mHandler.sendMessage(msg);
760    }
761
762    private void getElementAttr(byte numAttr, int[] attrs) {
763        int i;
764        ArrayList<Integer> attrList = new ArrayList<Integer>();
765        for (i = 0; i < numAttr; ++i) {
766            attrList.add(attrs[i]);
767        }
768        Message msg = mHandler.obtainMessage(MESSAGE_GET_ELEM_ATTRS, numAttr, 0, attrList);
769        mHandler.sendMessage(msg);
770    }
771
772    private void registerNotification(int eventId, int param) {
773        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_NOTIFICATION, eventId, param);
774        mHandler.sendMessage(msg);
775    }
776
777    private void processRegisterNotification(int eventId, int param) {
778        switch (eventId) {
779            case EVT_PLAY_STATUS_CHANGED:
780                mPlayStatusChangedNT = NOTIFICATION_TYPE_INTERIM;
781                registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
782                                       convertPlayStateToPlayStatus(mCurrentPlayState));
783                break;
784
785            case EVT_TRACK_CHANGED:
786                mTrackChangedNT = NOTIFICATION_TYPE_INTERIM;
787                sendTrackChangedRsp();
788                break;
789
790            case EVT_PLAY_POS_CHANGED:
791                long songPosition = getPlayPosition();
792                mPlayPosChangedNT = NOTIFICATION_TYPE_INTERIM;
793                mPlaybackIntervalMs = (long)param * 1000L;
794                if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
795                    mNextPosMs = songPosition + mPlaybackIntervalMs;
796                    mPrevPosMs = songPosition - mPlaybackIntervalMs;
797                    if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
798                        Message msg = mHandler.obtainMessage(MESSAGE_PLAY_INTERVAL_TIMEOUT);
799                        mHandler.sendMessageDelayed(msg, mPlaybackIntervalMs);
800                    }
801                }
802                registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int)songPosition);
803                break;
804
805        }
806    }
807
808    private void handlePassthroughCmd(int id, int keyState) {
809        switch (id) {
810            case BluetoothAvrcp.PASSTHROUGH_ID_REWIND:
811                rewind(keyState);
812                break;
813            case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR:
814                fastForward(keyState);
815                break;
816        }
817    }
818
819    private void fastForward(int keyState) {
820        Message msg = mHandler.obtainMessage(MESSAGE_FAST_FORWARD, keyState, 0);
821        mHandler.sendMessage(msg);
822    }
823
824    private void rewind(int keyState) {
825        Message msg = mHandler.obtainMessage(MESSAGE_REWIND, keyState, 0);
826        mHandler.sendMessage(msg);
827    }
828
829    private void changePositionBy(long amount) {
830        long currentPosMs = getPlayPosition();
831        if (currentPosMs == -1L) return;
832        long newPosMs = Math.max(0L, currentPosMs + amount);
833        mRemoteController.seekTo(newPosMs);
834    }
835
836    private int getSkipMultiplier() {
837        long currentTime = SystemClock.elapsedRealtime();
838        long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
839        return (int) Math.min(MAX_MULTIPLIER_VALUE, multi);
840    }
841
842    private void sendTrackChangedRsp() {
843        byte[] track = new byte[TRACK_ID_SIZE];
844
845        /* If no track is currently selected, then return
846           0xFFFFFFFFFFFFFFFF in the interim response */
847        long trackNumberRsp = -1L;
848
849        if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
850            trackNumberRsp = mTrackNumber;
851        }
852
853        /* track is stored in big endian format */
854        for (int i = 0; i < TRACK_ID_SIZE; ++i) {
855            track[i] = (byte) (trackNumberRsp >> (56 - 8 * i));
856        }
857        registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
858    }
859
860    private long getPlayPosition() {
861        long songPosition = -1L;
862        if (mCurrentPosMs != RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
863            if (mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
864                songPosition = SystemClock.elapsedRealtime() -
865                               mPlayStartTimeMs + mCurrentPosMs;
866            } else {
867                songPosition = mCurrentPosMs;
868            }
869        }
870        if (DEBUG) Log.v(TAG, "position=" + songPosition);
871        return songPosition;
872    }
873
874    private String getAttributeString(int attrId) {
875        String attrStr = null;
876        switch (attrId) {
877            case MEDIA_ATTR_TITLE:
878                attrStr = mMetadata.trackTitle;
879                break;
880
881            case MEDIA_ATTR_ARTIST:
882                attrStr = mMetadata.artist;
883                break;
884
885            case MEDIA_ATTR_ALBUM:
886                attrStr = mMetadata.albumTitle;
887                break;
888
889            case MEDIA_ATTR_PLAYING_TIME:
890                if (mSongLengthMs != 0L) {
891                    attrStr = Long.toString(mSongLengthMs);
892                }
893                break;
894
895        }
896        if (attrStr == null) {
897            attrStr = new String();
898        }
899        Log.v(TAG, "getAttributeString:attrId=" + attrId + " str=" + attrStr);
900        return attrStr;
901    }
902
903    private int convertPlayStateToPlayStatus(int playState) {
904        int playStatus = PLAYSTATUS_ERROR;
905        switch (playState) {
906            case RemoteControlClient.PLAYSTATE_PLAYING:
907            case RemoteControlClient.PLAYSTATE_BUFFERING:
908                playStatus = PLAYSTATUS_PLAYING;
909                break;
910
911            case RemoteControlClient.PLAYSTATE_STOPPED:
912            case RemoteControlClient.PLAYSTATE_NONE:
913                playStatus = PLAYSTATUS_STOPPED;
914                break;
915
916            case RemoteControlClient.PLAYSTATE_PAUSED:
917                playStatus = PLAYSTATUS_PAUSED;
918                break;
919
920            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
921            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
922                playStatus = PLAYSTATUS_FWD_SEEK;
923                break;
924
925            case RemoteControlClient.PLAYSTATE_REWINDING:
926            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
927                playStatus = PLAYSTATUS_REV_SEEK;
928                break;
929
930            case RemoteControlClient.PLAYSTATE_ERROR:
931                playStatus = PLAYSTATUS_ERROR;
932                break;
933
934        }
935        return playStatus;
936    }
937
938    private boolean isPlayingState(int playState) {
939        boolean isPlaying = false;
940        switch (playState) {
941            case RemoteControlClient.PLAYSTATE_PLAYING:
942            case RemoteControlClient.PLAYSTATE_BUFFERING:
943                isPlaying = true;
944                break;
945            default:
946                isPlaying = false;
947                break;
948        }
949        return isPlaying;
950    }
951
952    /**
953     * This is called from AudioService. It will return whether this device supports abs volume.
954     * NOT USED AT THE MOMENT.
955     */
956    public boolean isAbsoluteVolumeSupported() {
957        return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
958    }
959
960    /**
961     * We get this call from AudioService. This will send a message to our handler object,
962     * requesting our handler to call setVolumeNative()
963     */
964    public void adjustVolume(int direction) {
965        Message msg = mHandler.obtainMessage(MESSAGE_ADJUST_VOLUME, direction, 0);
966        mHandler.sendMessage(msg);
967    }
968
969    public void setAbsoluteVolume(int volume) {
970        if (volume == mLocalVolume) {
971            if (DEBUG) Log.v(TAG, "setAbsoluteVolume is setting same index, ignore "+volume);
972            return;
973        }
974
975        mHandler.removeMessages(MESSAGE_ADJUST_VOLUME);
976        Message msg = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, volume, 0);
977        mHandler.sendMessage(msg);
978    }
979
980    /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
981     * case when the volume is change locally on the carkit. This notification is not called when
982     * the volume is changed from the phone.
983     *
984     * This method will send a message to our handler to change the local stored volume and notify
985     * AudioService to update the UI
986     */
987    private void volumeChangeCallback(int volume, int ctype) {
988        Message msg = mHandler.obtainMessage(MESSAGE_VOLUME_CHANGED, volume, ctype);
989        mHandler.sendMessage(msg);
990    }
991
992    private void notifyVolumeChanged(int volume) {
993        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
994                      AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
995    }
996
997    private int convertToAudioStreamVolume(int volume) {
998        // Rescale volume to match AudioSystem's volume
999        return (int) Math.floor((double) volume*mAudioStreamMax/AVRCP_MAX_VOL);
1000    }
1001
1002    private int convertToAvrcpVolume(int volume) {
1003        return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);
1004    }
1005
1006    private void blackListCurrentDevice() {
1007        mFeatures &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
1008        mAudioManager.avrcpSupportsAbsoluteVolume(mAddress, isAbsoluteVolumeSupported());
1009
1010        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1011                Context.MODE_PRIVATE);
1012        SharedPreferences.Editor editor = pref.edit();
1013        editor.putBoolean(mAddress, true);
1014        editor.commit();
1015    }
1016
1017    private int modifyRcFeatureFromBlacklist(int feature, String address) {
1018        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1019                Context.MODE_PRIVATE);
1020        if (!pref.contains(address)) {
1021            return feature;
1022        }
1023        if (pref.getBoolean(address, false)) {
1024            feature &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
1025        }
1026        return feature;
1027    }
1028
1029    public void resetBlackList(String address) {
1030        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1031                Context.MODE_PRIVATE);
1032        SharedPreferences.Editor editor = pref.edit();
1033        editor.remove(address);
1034        editor.commit();
1035    }
1036
1037    /**
1038     * This is called from A2dpStateMachine to set A2dp audio state.
1039     */
1040    public void setA2dpAudioState(int state) {
1041        Message msg = mHandler.obtainMessage(MESSAGE_SET_A2DP_AUDIO_STATE, state, 0);
1042        mHandler.sendMessage(msg);
1043    }
1044
1045    public void dump(StringBuilder sb) {
1046        sb.append("AVRCP:\n");
1047        ProfileService.println(sb, "mMetadata: " + mMetadata);
1048        ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
1049        ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
1050        ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
1051        ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
1052        ProfileService.println(sb, "mTrackNumber: " + mTrackNumber);
1053        ProfileService.println(sb, "mCurrentPosMs: " + mCurrentPosMs);
1054        ProfileService.println(sb, "mPlayStartTimeMs: " + mPlayStartTimeMs);
1055        ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
1056        ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
1057        ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
1058        ProfileService.println(sb, "mNextPosMs: " + mNextPosMs);
1059        ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs);
1060        ProfileService.println(sb, "mSkipStartTime: " + mSkipStartTime);
1061        ProfileService.println(sb, "mFeatures: " + mFeatures);
1062        ProfileService.println(sb, "mRemoteVolume: " + mRemoteVolume);
1063        ProfileService.println(sb, "mLastRemoteVolume: " + mLastRemoteVolume);
1064        ProfileService.println(sb, "mLastDirection: " + mLastDirection);
1065        ProfileService.println(sb, "mVolumeStep: " + mVolumeStep);
1066        ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax);
1067        ProfileService.println(sb, "mVolCmdAdjustInProgress: " + mVolCmdAdjustInProgress);
1068        ProfileService.println(sb, "mVolCmdSetInProgress: " + mVolCmdSetInProgress);
1069        ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes);
1070        ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
1071        ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString());
1072    }
1073
1074    // Do not modify without updating the HAL bt_rc.h files.
1075
1076    // match up with btrc_play_status_t enum of bt_rc.h
1077    final static int PLAYSTATUS_STOPPED = 0;
1078    final static int PLAYSTATUS_PLAYING = 1;
1079    final static int PLAYSTATUS_PAUSED = 2;
1080    final static int PLAYSTATUS_FWD_SEEK = 3;
1081    final static int PLAYSTATUS_REV_SEEK = 4;
1082    final static int PLAYSTATUS_ERROR = 255;
1083
1084    // match up with btrc_media_attr_t enum of bt_rc.h
1085    final static int MEDIA_ATTR_TITLE = 1;
1086    final static int MEDIA_ATTR_ARTIST = 2;
1087    final static int MEDIA_ATTR_ALBUM = 3;
1088    final static int MEDIA_ATTR_TRACK_NUM = 4;
1089    final static int MEDIA_ATTR_NUM_TRACKS = 5;
1090    final static int MEDIA_ATTR_GENRE = 6;
1091    final static int MEDIA_ATTR_PLAYING_TIME = 7;
1092
1093    // match up with btrc_event_id_t enum of bt_rc.h
1094    final static int EVT_PLAY_STATUS_CHANGED = 1;
1095    final static int EVT_TRACK_CHANGED = 2;
1096    final static int EVT_TRACK_REACHED_END = 3;
1097    final static int EVT_TRACK_REACHED_START = 4;
1098    final static int EVT_PLAY_POS_CHANGED = 5;
1099    final static int EVT_BATT_STATUS_CHANGED = 6;
1100    final static int EVT_SYSTEM_STATUS_CHANGED = 7;
1101    final static int EVT_APP_SETTINGS_CHANGED = 8;
1102
1103    // match up with btrc_notification_type_t enum of bt_rc.h
1104    final static int NOTIFICATION_TYPE_INTERIM = 0;
1105    final static int NOTIFICATION_TYPE_CHANGED = 1;
1106
1107    // match up with BTRC_UID_SIZE of bt_rc.h
1108    final static int TRACK_ID_SIZE = 8;
1109
1110    private native static void classInitNative();
1111    private native void initNative();
1112    private native void cleanupNative();
1113    private native boolean getPlayStatusRspNative(int playStatus, int songLen, int songPos);
1114    private native boolean getElementAttrRspNative(byte numAttr, int[] attrIds, String[] textArray);
1115    private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
1116    private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
1117    private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
1118    private native boolean setVolumeNative(int volume);
1119    private native boolean sendPassThroughCommandNative(int keyCode, int keyState);
1120
1121}
1122