Avrcp.java revision 601a396cdc635a5a6dc5a528c5b775e960822387
1/*
2 * Copyright (C) 2016 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 android.bluetooth.BluetoothA2dp;
20import android.bluetooth.BluetoothAvrcp;
21import android.bluetooth.BluetoothDevice;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.pm.ApplicationInfo;
28import android.content.pm.PackageManager;
29import android.content.pm.PackageManager.NameNotFoundException;
30import android.content.pm.ResolveInfo;
31import android.content.res.Resources;
32import android.content.SharedPreferences;
33import android.media.AudioManager;
34import android.media.MediaDescription;
35import android.media.MediaMetadata;
36import android.media.session.MediaController;
37import android.media.session.MediaSession;
38import android.media.session.MediaSession.QueueItem;
39import android.media.session.MediaSessionManager;
40import android.media.session.PlaybackState;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.HandlerThread;
44import android.os.Looper;
45import android.os.Message;
46import android.os.SystemClock;
47import android.util.Log;
48import android.view.KeyEvent;
49
50import com.android.bluetooth.btservice.ProfileService;
51import com.android.bluetooth.R;
52import com.android.bluetooth.Utils;
53
54import java.util.ArrayList;
55import java.util.HashMap;
56import java.util.Iterator;
57import java.util.List;
58import java.util.Map;
59
60/******************************************************************************
61 * support Bluetooth AVRCP profile. support metadata, play status, event
62 * notifications, address player selection and browse feature implementation.
63 ******************************************************************************/
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 MediaSessionManager mMediaSessionManager;
74    private MediaController mMediaController;
75    private MediaControllerListener mMediaControllerCb;
76    private MediaAttributes mMediaAttributes;
77    private PackageManager mPackageManager;
78    private int mTransportControlFlags;
79    private PlaybackState mCurrentPlayState;
80    private long mLastStateUpdate;
81    private int mPlayStatusChangedNT;
82    private int mTrackChangedNT;
83    private int mPlayPosChangedNT;
84    private long mTrackNumber;
85    private long mSongLengthMs;
86    private long mPlaybackIntervalMs;
87    private long mLastReportedPosition;
88    private long mNextPosMs;
89    private long mPrevPosMs;
90    private long mSkipStartTime;
91    private int mFeatures;
92    private int mRemoteVolume;
93    private int mLastRemoteVolume;
94    private int mInitialRemoteVolume;
95
96    /* Local volume in audio index 0-15 */
97    private int mLocalVolume;
98    private int mLastLocalVolume;
99    private int mAbsVolThreshold;
100
101    private String mAddress;
102    private HashMap<Integer, Integer> mVolumeMapping;
103
104    private int mLastDirection;
105    private final int mVolumeStep;
106    private final int mAudioStreamMax;
107    private boolean mVolCmdAdjustInProgress;
108    private boolean mVolCmdSetInProgress;
109    private int mAbsVolRetryTimes;
110    private int mSkipAmount;
111    private int mCurrAddrPlayerID;
112    private int mCurrBrowsePlayerID;
113    private MediaPlayerListRsp mMPLObj;
114    private AvrcpMediaRsp mAvrcpMediaRsp;
115
116    /* UID counter to be shared across different files. */
117    static short sUIDCounter;
118
119    /* BTRC features */
120    public static final int BTRC_FEAT_METADATA = 0x01;
121    public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
122    public static final int BTRC_FEAT_BROWSE = 0x04;
123
124    /* AVRC response codes, from avrc_defs */
125    private static final int AVRC_RSP_NOT_IMPL = 8;
126    private static final int AVRC_RSP_ACCEPT = 9;
127    private static final int AVRC_RSP_REJ = 10;
128    private static final int AVRC_RSP_IN_TRANS = 11;
129    private static final int AVRC_RSP_IMPL_STBL = 12;
130    private static final int AVRC_RSP_CHANGED = 13;
131    private static final int AVRC_RSP_INTERIM = 15;
132
133    /* AVRC request commands from Native */
134    private static final int MSG_NATIVE_REQ_GET_RC_FEATURES = 1;
135    private static final int MSG_NATIVE_REQ_GET_PLAY_STATUS = 2;
136    private static final int MSG_NATIVE_REQ_GET_ELEM_ATTRS = 3;
137    private static final int MSG_NATIVE_REQ_REGISTER_NOTIFICATION = 4;
138    private static final int MSG_NATIVE_REQ_VOLUME_CHANGE = 5;
139    private static final int MSG_NATIVE_REQ_GET_FOLDER_ITEMS = 6;
140    private static final int MSG_NATIVE_REQ_SET_ADDR_PLAYER = 7;
141    private static final int MSG_NATIVE_REQ_SET_BR_PLAYER = 8;
142    private static final int MSG_NATIVE_REQ_CHANGE_PATH = 9;
143    private static final int MSG_NATIVE_REQ_PLAY_ITEM = 10;
144    private static final int MSG_NATIVE_REQ_GET_ITEM_ATTR = 11;
145    private static final int MSG_NATIVE_REQ_GET_TOTAL_NUM_OF_ITEMS = 12;
146    private static final int MSG_NATIVE_REQ_PASS_THROUGH = 13;
147
148    /* other AVRC messages */
149    private static final int MSG_PLAY_INTERVAL_TIMEOUT = 14;
150    private static final int MSG_ADJUST_VOLUME = 15;
151    private static final int MSG_SET_ABSOLUTE_VOLUME = 16;
152    private static final int MSG_ABS_VOL_TIMEOUT = 17;
153    private static final int MSG_FAST_FORWARD = 18;
154    private static final int MSG_REWIND = 19;
155    private static final int MSG_CHANGE_PLAY_POS = 20;
156    private static final int MSG_SET_A2DP_AUDIO_STATE = 21;
157
158    private static final int BUTTON_TIMEOUT_TIME = 2000;
159    private static final int BASE_SKIP_AMOUNT = 2000;
160    private static final int SKIP_PERIOD = 400;
161    private static final int SKIP_DOUBLE_INTERVAL = 3000;
162    private static final long MAX_MULTIPLIER_VALUE = 128L;
163    private static final int CMD_TIMEOUT_DELAY = 2000;
164    private static final int MAX_ERROR_RETRY_TIMES = 6;
165    private static final int AVRCP_MAX_VOL = 127;
166    private static final int AVRCP_BASE_VOLUME_STEP = 1;
167
168    /* Communicates with MediaPlayer to fetch media content */
169    private BrowsedMediaPlayer mBrowsedMediaPlayer;
170
171    /* Addressed player */
172    private AddressedMediaPlayer mAddressedMediaPlayer;
173
174    /* List of Media player instances, useful for retrieving MediaPlayerList or MediaPlayerInfo */
175    private ArrayList<MediaPlayerInfo> mMediaPlayerInfoList;
176
177    /* List of media players which supports browse */
178    private ArrayList<BrowsePlayerInfo> mBrowsePlayerInfoList;
179
180    /* Manage browsed players */
181    private AvrcpBrowseManager mAvrcpBrowseManager;
182
183    /* Broadcast receiver for device connections intent broadcasts */
184    private final BroadcastReceiver mAvrcpReceiver = new AvrcpServiceBroadcastReceiver();
185
186    static {
187        classInitNative();
188    }
189
190    private Avrcp(Context context) {
191        mMediaAttributes = new MediaAttributes(null);
192        mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
193        mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
194        mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
195        mTrackNumber = -1L;
196        mLastStateUpdate = -1L;
197        mSongLengthMs = 0L;
198        mPlaybackIntervalMs = 0L;
199        mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
200        mLastReportedPosition = -1;
201        mNextPosMs = -1;
202        mPrevPosMs = -1;
203        mFeatures = 0;
204        mRemoteVolume = -1;
205        mInitialRemoteVolume = -1;
206        mLastRemoteVolume = -1;
207        mLastDirection = 0;
208        mVolCmdAdjustInProgress = false;
209        mVolCmdSetInProgress = false;
210        mAbsVolRetryTimes = 0;
211        mLocalVolume = -1;
212        mLastLocalVolume = -1;
213        mAbsVolThreshold = 0;
214        mVolumeMapping = new HashMap<Integer, Integer>();
215        sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER;
216        mCurrAddrPlayerID = -1;
217        mCurrBrowsePlayerID = -1;
218        mContext = context;
219        mMPLObj = null;
220        mAddressedMediaPlayer = null;
221
222        initNative();
223
224        mMediaSessionManager = (MediaSessionManager) context.getSystemService(
225            Context.MEDIA_SESSION_SERVICE);
226        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
227        mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
228        mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax);
229
230        Resources resources = context.getResources();
231        if (resources != null) {
232            mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
233        }
234
235        // Register for package removal intent broadcasts for media button receiver persistence
236        IntentFilter pkgFilter = new IntentFilter();
237        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
238        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
239        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
240        pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
241        pkgFilter.addDataScheme("package");
242        context.registerReceiver(mAvrcpReceiver, pkgFilter);
243    }
244
245    private void start() {
246        HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
247        thread.start();
248        Looper looper = thread.getLooper();
249        mHandler = new AvrcpMessageHandler(looper);
250        mMediaControllerCb = new MediaControllerListener();
251        mAvrcpMediaRsp = new AvrcpMediaRsp();
252        mMediaPlayerInfoList = new ArrayList<MediaPlayerInfo>();
253        mBrowsePlayerInfoList = new ArrayList<BrowsePlayerInfo>();
254        mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveSessionListener, null,
255                mHandler);
256        mPackageManager = mContext.getApplicationContext().getPackageManager();
257
258        /* create object to communicate with addressed player */
259        mAddressedMediaPlayer = new AddressedMediaPlayer(mAvrcpMediaRsp);
260
261        /* initializing media player's list */
262        buildBrowsablePlayersList();
263        buildMediaPlayersList();
264
265        /* initialize BrowseMananger which manages Browse commands and response */
266        mAvrcpBrowseManager = new AvrcpBrowseManager(mContext, mAvrcpMediaRsp);
267    }
268
269    public static Avrcp make(Context context) {
270        if (DEBUG) Log.v(TAG, "make");
271        Avrcp ar = new Avrcp(context);
272        ar.start();
273        return ar;
274    }
275
276    public void doQuit() {
277        if (DEBUG) Log.d(TAG, "doQuit");
278        mHandler.removeCallbacksAndMessages(null);
279        Looper looper = mHandler.getLooper();
280        if (looper != null) {
281            looper.quit();
282        }
283
284        unregOldMediaControllerCb();
285        mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveSessionListener);
286
287        mHandler = null;
288        mMPLObj = null;
289        mContext.unregisterReceiver(mAvrcpReceiver);
290
291        mAddressedMediaPlayer.cleanup();
292        mAvrcpBrowseManager.cleanup();
293    }
294
295    public void cleanup() {
296        if (DEBUG) Log.d(TAG, "cleanup");
297        cleanupNative();
298        if (mVolumeMapping != null)
299            mVolumeMapping.clear();
300    }
301
302    private class MediaControllerListener extends MediaController.Callback {
303        @Override
304        public void onMetadataChanged(MediaMetadata metadata) {
305            Log.v(TAG, "MediaController metadata changed");
306            updateMetadata(metadata);
307        }
308
309        @Override
310        public synchronized void onPlaybackStateChanged(PlaybackState state) {
311            Log.v(TAG, "MediaController playback changed: " + state.toString());
312
313            updatePlaybackState(state);
314
315            if (DEBUG) Log.v(TAG, "onPlaybackStateChanged: state=" + state.getState());
316            byte stateBytes = (byte) convertPlayStateToBytes(state.getState());
317
318            /* updating play status in global media player list */
319            if (!isCurrentMediaPlayerListEmpty() && isIdValid(mCurrAddrPlayerID)) {
320                try {
321                    mMediaPlayerInfoList.get(mCurrAddrPlayerID - 1).setPlayStatus(stateBytes);
322                } catch (IndexOutOfBoundsException e) {
323                    Log.i(TAG, "onPlaybackStateChanged: list size = " + getPlayerListSize() +
324                            ", mCurrAddrPlayerID = " + mCurrAddrPlayerID);
325                    e.printStackTrace();
326                }
327            }
328
329        }
330
331        @Override
332        public void onSessionDestroyed() {
333            Log.v(TAG, "MediaController session destroyed");
334        }
335
336        @Override
337        public void onQueueChanged(List<MediaSession.QueueItem> queue) {
338            if (queue == null) {
339                Log.v(TAG, "onQueueChanged: received null queue");
340                return;
341            }
342
343            Log.v(TAG, "onQueueChanged: NowPlaying list changed, Queue Size = "+ queue.size());
344            mAddressedMediaPlayer.updateNowPlayingList(queue);
345
346            /* sent notification to remote for NowPlayingList changed */
347            if(!registerNotificationRspNowPlayingChangedNative(
348                    AvrcpConstants.NOTIFICATION_TYPE_CHANGED)){
349                Log.e(TAG, "onQueueChanged-registerNotificationRspNowPlayingChangedNative failed");
350            }
351        }
352    }
353
354    /** Handles Avrcp messages. */
355    private final class AvrcpMessageHandler extends Handler {
356        private AvrcpMessageHandler(Looper looper) {
357            super(looper);
358        }
359
360        @Override
361        public void handleMessage(Message msg) {
362            if (DEBUG) Log.v(TAG, "AvrcpMessageHandler: received message=" + msg.what);
363
364            switch (msg.what) {
365            case MSG_NATIVE_REQ_GET_RC_FEATURES:
366            {
367                String address = (String) msg.obj;
368                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_RC_FEATURES: address="+address+
369                        ", features="+msg.arg1);
370                mFeatures = msg.arg1;
371                mFeatures = modifyRcFeatureFromBlacklist(mFeatures, address);
372                mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported());
373                mLastLocalVolume = -1;
374                mRemoteVolume = -1;
375                mLocalVolume = -1;
376                mInitialRemoteVolume = -1;
377                mAddress = address;
378                if (mVolumeMapping != null)
379                    mVolumeMapping.clear();
380                break;
381            }
382
383            case MSG_NATIVE_REQ_GET_PLAY_STATUS:
384            {
385                byte[] address = (byte[]) msg.obj;
386                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_PLAY_STATUS");
387                getPlayStatusRspNative(address, convertPlayStateToPlayStatus(mCurrentPlayState),
388                        (int) mSongLengthMs, (int) getPlayPosition());
389                break;
390            }
391
392            case MSG_NATIVE_REQ_GET_ELEM_ATTRS:
393            {
394                String[] textArray;
395                AvrcpCmd.ElementAttrCmd elem = (AvrcpCmd.ElementAttrCmd) msg.obj;
396                byte numAttr = elem.mNumAttr;
397                int[] attrIds = elem.mAttrIDs;
398                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_ELEM_ATTRS:numAttr=" + numAttr);
399                textArray = new String[numAttr];
400                for (int i = 0; i < numAttr; ++i) {
401                    textArray[i] = mMediaAttributes.getString(attrIds[i]);
402                    Log.v(TAG, "getAttributeString:attrId=" + attrIds[i] +
403                            " str=" + textArray[i]);
404                }
405                byte[] bdaddr = elem.mAddress;
406                getElementAttrRspNative(bdaddr, numAttr, attrIds, textArray);
407                break;
408            }
409
410            case MSG_NATIVE_REQ_REGISTER_NOTIFICATION:
411                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_REGISTER_NOTIFICATION:event=" + msg.arg1 +
412                        " param=" + msg.arg2);
413                processRegisterNotification((byte[]) msg.obj, msg.arg1, msg.arg2);
414                break;
415
416            case MSG_PLAY_INTERVAL_TIMEOUT:
417                if (DEBUG) Log.v(TAG, "MSG_PLAY_INTERVAL_TIMEOUT");
418                sendPlayPosNotificationRsp(false);
419                break;
420
421            case MSG_NATIVE_REQ_VOLUME_CHANGE:
422                if (!isAbsoluteVolumeSupported()) {
423                    if (DEBUG) Log.v(TAG, "ignore MSG_NATIVE_REQ_VOLUME_CHANGE");
424                    break;
425                }
426
427                if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_VOLUME_CHANGE: volume=" + ((byte) msg.arg1 & 0x7f)
428                        + " ctype=" + msg.arg2);
429
430                boolean volAdj = false;
431                if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) {
432                    if (mVolCmdAdjustInProgress == false && mVolCmdSetInProgress == false) {
433                        Log.e(TAG, "Unsolicited response, ignored");
434                        break;
435                    }
436                    removeMessages(MSG_ABS_VOL_TIMEOUT);
437
438                    volAdj = mVolCmdAdjustInProgress;
439                    mVolCmdAdjustInProgress = false;
440                    mVolCmdSetInProgress = false;
441                    mAbsVolRetryTimes = 0;
442                }
443
444                byte absVol = (byte) ((byte) msg.arg1 & 0x7f); // discard MSB as it is RFD
445                // convert remote volume to local volume
446                int volIndex = convertToAudioStreamVolume(absVol);
447                if (mInitialRemoteVolume == -1) {
448                    mInitialRemoteVolume = absVol;
449                    if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax && volIndex > mAbsVolThreshold) {
450                        if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" + mAbsVolThreshold);
451                        Message msg1 = mHandler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME, mAbsVolThreshold , 0);
452                        mHandler.sendMessage(msg1);
453                        mRemoteVolume = absVol;
454                        mLocalVolume = volIndex;
455                        break;
456                    }
457                }
458
459                if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT ||
460                                                 msg.arg2 == AVRC_RSP_CHANGED ||
461                                                 msg.arg2 == AVRC_RSP_INTERIM)) {
462                    /* If the volume has successfully changed */
463                    mLocalVolume = volIndex;
464                    if (mLastLocalVolume != -1 && msg.arg2 == AVRC_RSP_ACCEPT) {
465                        if (mLastLocalVolume != volIndex) {
466                            /* remote volume changed more than requested due to
467                             * local and remote has different volume steps */
468                            if (DEBUG) Log.d(TAG, "Remote returned volume does not match desired volume "
469                                    + mLastLocalVolume + " vs " + volIndex);
470                            mLastLocalVolume = mLocalVolume;
471                        }
472                    }
473                    // remember the remote volume value, as it's the one supported by remote
474                    if (volAdj) {
475                        synchronized (mVolumeMapping) {
476                            mVolumeMapping.put(volIndex, (int) absVol);
477                            if (DEBUG) Log.v(TAG, "remember volume mapping " +volIndex+ "-"+absVol);
478                        }
479                    }
480
481                    notifyVolumeChanged(mLocalVolume);
482                    mRemoteVolume = absVol;
483                    long pecentVolChanged = ((long) absVol * 100) / 0x7f;
484                    Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%");
485                } else if (msg.arg2 == AVRC_RSP_REJ) {
486                    Log.e(TAG, "setAbsoluteVolume call rejected");
487                } else if (volAdj && mLastRemoteVolume > 0 && mLastRemoteVolume < AVRCP_MAX_VOL &&
488                        mLocalVolume == volIndex &&
489                        (msg.arg2 == AVRC_RSP_ACCEPT)) {
490                    /* oops, the volume is still same, remote does not like the value
491                     * retry a volume one step up/down */
492                    if (DEBUG) Log.d(TAG, "Remote device didn't tune volume, let's try one more step.");
493                    int retry_volume = Math.min(AVRCP_MAX_VOL,
494                            Math.max(0, mLastRemoteVolume + mLastDirection));
495                    if (setVolumeNative(retry_volume)) {
496                        mLastRemoteVolume = retry_volume;
497                        sendMessageDelayed(obtainMessage(MSG_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
498                        mVolCmdAdjustInProgress = true;
499                    }
500                }
501                break;
502
503            case MSG_ADJUST_VOLUME:
504                if (!isAbsoluteVolumeSupported()) {
505                    if (DEBUG) Log.v(TAG, "ignore MSG_ADJUST_VOLUME");
506                    break;
507                }
508
509                if (DEBUG) Log.d(TAG, "MSG_ADJUST_VOLUME: direction=" + msg.arg1);
510
511                if (mVolCmdAdjustInProgress || mVolCmdSetInProgress) {
512                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
513                    break;
514                }
515
516                // Remote device didn't set initial volume. Let's black list it
517                if (mInitialRemoteVolume == -1) {
518                    Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
519                    blackListCurrentDevice();
520                    break;
521                }
522
523                // Wait on verification on volume from device, before changing the volume.
524                if (mRemoteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) {
525                    int setVol = -1;
526                    int targetVolIndex = -1;
527                    if (mLocalVolume == 0 && msg.arg1 == -1) {
528                        if (DEBUG) Log.w(TAG, "No need to Vol down from 0.");
529                        break;
530                    }
531                    if (mLocalVolume == mAudioStreamMax && msg.arg1 == 1) {
532                        if (DEBUG) Log.w(TAG, "No need to Vol up from max.");
533                        break;
534                    }
535
536                    targetVolIndex = mLocalVolume + msg.arg1;
537                    if (DEBUG) Log.d(TAG, "Adjusting volume to  " + targetVolIndex);
538
539                    Integer i;
540                    synchronized (mVolumeMapping) {
541                        i = mVolumeMapping.get(targetVolIndex);
542                    }
543
544                    if (i != null) {
545                        /* if we already know this volume mapping, use it */
546                        setVol = i.byteValue();
547                        if (setVol == mRemoteVolume) {
548                            if (DEBUG) Log.d(TAG, "got same volume from mapping for " + targetVolIndex + ", ignore.");
549                            setVol = -1;
550                        }
551                        if (DEBUG) Log.d(TAG, "set volume from mapping " + targetVolIndex + "-" + setVol);
552                    }
553
554                    if (setVol == -1) {
555                        /* otherwise use phone steps */
556                        setVol = Math.min(AVRCP_MAX_VOL,
557                                convertToAvrcpVolume(Math.max(0, targetVolIndex)));
558                        if (DEBUG) Log.d(TAG, "set volume from local volume "+ targetVolIndex+"-"+ setVol);
559                    }
560
561                    if (setVolumeNative(setVol)) {
562                        sendMessageDelayed(obtainMessage(MSG_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
563                        mVolCmdAdjustInProgress = true;
564                        mLastDirection = msg.arg1;
565                        mLastRemoteVolume = setVol;
566                        mLastLocalVolume = targetVolIndex;
567                    } else {
568                         if (DEBUG) Log.d(TAG, "setVolumeNative failed");
569                    }
570                } else {
571                    Log.e(TAG, "Unknown direction in MSG_ADJUST_VOLUME");
572                }
573                break;
574
575            case MSG_SET_ABSOLUTE_VOLUME:
576                if (!isAbsoluteVolumeSupported()) {
577                    if (DEBUG) Log.v(TAG, "ignore MSG_SET_ABSOLUTE_VOLUME");
578                    break;
579                }
580
581                if (DEBUG) Log.v(TAG, "MSG_SET_ABSOLUTE_VOLUME");
582
583                if (mVolCmdSetInProgress || mVolCmdAdjustInProgress) {
584                    if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
585                    break;
586                }
587
588                // Remote device didn't set initial volume. Let's black list it
589                if (mInitialRemoteVolume == -1) {
590                    if (DEBUG) Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
591                    blackListCurrentDevice();
592                    break;
593                }
594
595                int avrcpVolume = convertToAvrcpVolume(msg.arg1);
596                avrcpVolume = Math.min(AVRCP_MAX_VOL, Math.max(0, avrcpVolume));
597                if (DEBUG) Log.d(TAG, "Setting volume to " + msg.arg1 + "-" + avrcpVolume);
598                if (setVolumeNative(avrcpVolume)) {
599                    sendMessageDelayed(obtainMessage(MSG_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
600                    mVolCmdSetInProgress = true;
601                    mLastRemoteVolume = avrcpVolume;
602                    mLastLocalVolume = msg.arg1;
603                } else {
604                     if (DEBUG) Log.d(TAG, "setVolumeNative failed");
605                }
606                break;
607
608            case MSG_ABS_VOL_TIMEOUT:
609                if (DEBUG) Log.v(TAG, "MSG_ABS_VOL_TIMEOUT: Volume change cmd timed out.");
610                mVolCmdAdjustInProgress = false;
611                mVolCmdSetInProgress = false;
612                if (mAbsVolRetryTimes >= MAX_ERROR_RETRY_TIMES) {
613                    mAbsVolRetryTimes = 0;
614                    /* too many volume change failures, black list the device */
615                    blackListCurrentDevice();
616                } else {
617                    mAbsVolRetryTimes += 1;
618                    if (setVolumeNative(mLastRemoteVolume)) {
619                        sendMessageDelayed(obtainMessage(MSG_ABS_VOL_TIMEOUT), CMD_TIMEOUT_DELAY);
620                        mVolCmdSetInProgress = true;
621                    }
622                }
623                break;
624
625            case MSG_FAST_FORWARD:
626            case MSG_REWIND:
627                if (msg.what == MSG_FAST_FORWARD) {
628                    if ((mCurrentPlayState.getActions() &
629                            PlaybackState.ACTION_FAST_FORWARD) != 0) {
630                        int keyState = msg.arg1 == AvrcpConstants.KEY_STATE_PRESS ?
631                                KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
632                        KeyEvent keyEvent =
633                                new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
634                        mMediaController.dispatchMediaButtonEvent(keyEvent);
635                        break;
636                    }
637                } else if ((mCurrentPlayState.getActions() &
638                            PlaybackState.ACTION_REWIND) != 0) {
639                    int keyState = msg.arg1 == AvrcpConstants.KEY_STATE_PRESS ?
640                            KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
641                    KeyEvent keyEvent =
642                            new KeyEvent(keyState, KeyEvent.KEYCODE_MEDIA_REWIND);
643                    mMediaController.dispatchMediaButtonEvent(keyEvent);
644                    break;
645                }
646
647                int skipAmount;
648                int playStatus;
649                if (msg.what == MSG_FAST_FORWARD) {
650                    if (DEBUG) Log.v(TAG, "MSG_FAST_FORWARD");
651                    removeMessages(MSG_FAST_FORWARD);
652                    skipAmount = BASE_SKIP_AMOUNT;
653                    playStatus = PLAYSTATUS_FWD_SEEK;
654                } else {
655                    if (DEBUG) Log.v(TAG, "MSG_REWIND");
656                    removeMessages(MSG_REWIND);
657                    skipAmount = -BASE_SKIP_AMOUNT;
658                    playStatus = PLAYSTATUS_REV_SEEK;
659                }
660
661                if (hasMessages(MSG_CHANGE_PLAY_POS) &&
662                        (skipAmount != mSkipAmount)) {
663                    Log.w(TAG, "missing release button event:" + mSkipAmount);
664                }
665
666                if ((!hasMessages(MSG_CHANGE_PLAY_POS)) ||
667                        (skipAmount != mSkipAmount)) {
668                    mSkipStartTime = SystemClock.elapsedRealtime();
669                }
670
671                removeMessages(MSG_CHANGE_PLAY_POS);
672                if (msg.arg1 == AvrcpConstants.KEY_STATE_PRESS) {
673                    mSkipAmount = skipAmount;
674                    changePositionBy(mSkipAmount * getSkipMultiplier());
675                    Message posMsg = obtainMessage(MSG_CHANGE_PLAY_POS);
676                    posMsg.arg1 = 1;
677                    sendMessageDelayed(posMsg, SKIP_PERIOD);
678                }
679
680                registerNotificationRspPlayStatusNative(
681                        AvrcpConstants.NOTIFICATION_TYPE_CHANGED, playStatus);
682
683                break;
684
685            case MSG_CHANGE_PLAY_POS:
686                if (DEBUG) Log.v(TAG, "MSG_CHANGE_PLAY_POS:" + msg.arg1);
687                changePositionBy(mSkipAmount * getSkipMultiplier());
688                if (msg.arg1 * SKIP_PERIOD < BUTTON_TIMEOUT_TIME) {
689                    Message posMsg = obtainMessage(MSG_CHANGE_PLAY_POS);
690                    posMsg.arg1 = msg.arg1 + 1;
691                    sendMessageDelayed(posMsg, SKIP_PERIOD);
692                }
693                break;
694
695            case MSG_SET_A2DP_AUDIO_STATE:
696                if (DEBUG) Log.v(TAG, "MSG_SET_A2DP_AUDIO_STATE:" + msg.arg1);
697                updateA2dpAudioState(msg.arg1);
698                break;
699
700            case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
701                AvrcpCmd.FolderItemsCmd folderObj = (AvrcpCmd.FolderItemsCmd) msg.obj;
702                switch (folderObj.mScope) {
703                    case AvrcpConstants.BTRC_SCOPE_PLAYER_LIST:
704                        handleMediaPlayerListRsp(folderObj);
705                        break;
706                    case AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM:
707                    case AvrcpConstants.BTRC_SCOPE_NOW_PLAYING:
708                        handleGetFolderItemBrowseResponse(folderObj, folderObj.mAddress);
709                        break;
710                    default:
711                        Log.e(TAG, "unknown scope for getfolderitems. scope = "
712                                + folderObj.mScope);
713                        getFolderItemsRspNative(folderObj.mAddress,
714                                AvrcpConstants.RSP_INV_SCOPE, (short) 0, (byte) 0, 0,
715                                null, null, null, null, null, null, null, null);
716                }
717                break;
718            }
719
720            case MSG_NATIVE_REQ_SET_ADDR_PLAYER:
721                // object is bdaddr, argument 1 is the selected player id
722                setAddressedPlayer((byte[]) msg.obj, msg.arg1);
723                break;
724
725            case MSG_NATIVE_REQ_GET_ITEM_ATTR:
726                // msg object contains the item attribute object
727                handleGetItemAttr((AvrcpCmd.ItemAttrCmd) msg.obj);
728                break;
729
730            case MSG_NATIVE_REQ_SET_BR_PLAYER:
731                // argument 1 is the selected player id
732                setBrowsedPlayer((byte[]) msg.obj, msg.arg1);
733                break;
734
735            case MSG_NATIVE_REQ_CHANGE_PATH:
736            {
737                Bundle data = msg.getData();
738                byte[] bdaddr = data.getByteArray("BdAddress");
739                byte[] folderUid = data.getByteArray("folderUid");
740                byte direction = data.getByte("direction");
741                if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) {
742                        mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).changePath(folderUid,
743                        direction);
744                } else {
745                    Log.e(TAG, "Remote requesting change path before setbrowsedplayer");
746                    changePathRspNative(bdaddr, AvrcpConstants.RSP_BAD_CMD, 0);
747                }
748                break;
749            }
750
751            case MSG_NATIVE_REQ_PLAY_ITEM:
752            {
753                Bundle data = msg.getData();
754                byte[] bdaddr = data.getByteArray("BdAddress");
755                byte[] uid = data.getByteArray("uid");
756                byte scope = data.getByte("scope");
757                handlePlayItemResponse(bdaddr, uid, scope);
758                break;
759            }
760
761            case MSG_NATIVE_REQ_GET_TOTAL_NUM_OF_ITEMS:
762                // argument 1 is scope, object is bdaddr
763                handleGetTotalNumOfItemsResponse((byte[]) msg.obj, (byte) msg.arg1);
764                break;
765
766            case MSG_NATIVE_REQ_PASS_THROUGH:
767                // argument 1 is id, argument 2 is keyState, object is bdaddr
768                mAddressedMediaPlayer.handlePassthroughCmd(msg.arg1, msg.arg2, (byte[]) msg.obj,
769                        mMediaController);
770                break;
771
772            default:
773                Log.e(TAG, "unknown message! msg.what=" + msg.what);
774                break;
775            }
776        }
777    }
778
779    private void updateA2dpAudioState(int state) {
780        boolean isPlaying = (state == BluetoothA2dp.STATE_PLAYING);
781        if (isPlaying != isPlayingState(mCurrentPlayState)) {
782            /* if a2dp is streaming, check to make sure music is active */
783            if (isPlaying && !mAudioManager.isMusicActive())
784                return;
785            PlaybackState.Builder builder = new PlaybackState.Builder();
786            if (isPlaying) {
787                builder.setState(PlaybackState.STATE_PLAYING,
788                        PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f);
789            } else {
790                builder.setState(PlaybackState.STATE_PAUSED,
791                        PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f);
792            }
793            updatePlaybackState(builder.build());
794        }
795    }
796
797    private void updatePlaybackState(PlaybackState state) {
798        if (state == null) {
799          state = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE,
800                PlaybackState.PLAYBACK_POSITION_UNKNOWN, 0.0f).build();
801        }
802
803        int oldPlayStatus = convertPlayStateToPlayStatus(mCurrentPlayState);
804        int newPlayStatus = convertPlayStateToPlayStatus(state);
805
806        if (DEBUG) {
807            Log.v(TAG, "updatePlaybackState (" + mPlayStatusChangedNT + "): "+
808                    "old=" + mCurrentPlayState + "(" + oldPlayStatus + "), "+
809                    "new=" + state + "(" + newPlayStatus + ")");
810        }
811
812        mCurrentPlayState = state;
813        mLastStateUpdate = SystemClock.elapsedRealtime();
814
815        sendPlayPosNotificationRsp(false);
816
817        if (mPlayStatusChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM &&
818                (oldPlayStatus != newPlayStatus)) {
819            mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
820            registerNotificationRspPlayStatusNative(mPlayStatusChangedNT, newPlayStatus);
821        }
822    }
823
824    private void updateTransportControls(int transportControlFlags) {
825        mTransportControlFlags = transportControlFlags;
826    }
827
828    class MediaAttributes {
829        private boolean exists;
830        private String title;
831        private String artistName;
832        private String albumName;
833        private String mediaNumber;
834        private String mediaTotalNumber;
835        private String genre;
836        private String playingTimeMs;
837
838        private static final int ATTR_TITLE = 1;
839        private static final int ATTR_ARTIST_NAME = 2;
840        private static final int ATTR_ALBUM_NAME = 3;
841        private static final int ATTR_MEDIA_NUMBER = 4;
842        private static final int ATTR_MEDIA_TOTAL_NUMBER = 5;
843        private static final int ATTR_GENRE = 6;
844        private static final int ATTR_PLAYING_TIME_MS = 7;
845
846
847        public MediaAttributes(MediaMetadata data) {
848            exists = data != null;
849            if (!exists)
850                return;
851
852            artistName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ARTIST));
853            albumName = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_ALBUM));
854            mediaNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
855            mediaTotalNumber = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
856            genre = stringOrBlank(data.getString(MediaMetadata.METADATA_KEY_GENRE));
857            playingTimeMs = longStringOrBlank(data.getLong(MediaMetadata.METADATA_KEY_DURATION));
858
859            // Try harder for the title.
860            title = data.getString(MediaMetadata.METADATA_KEY_TITLE);
861
862            if (title == null) {
863                MediaDescription desc = data.getDescription();
864                if (desc != null) {
865                    CharSequence val = desc.getDescription();
866                    if (val != null)
867                        title = val.toString();
868                }
869            }
870
871            if (title == null)
872                title = new String();
873        }
874
875        public boolean equals(MediaAttributes other) {
876            if (other == null)
877                return false;
878
879            if (exists != other.exists)
880                return false;
881
882            if (exists == false)
883                return true;
884
885            return (title.equals(other.title)) &&
886                (artistName.equals(other.artistName)) &&
887                (albumName.equals(other.albumName)) &&
888                (mediaNumber.equals(other.mediaNumber)) &&
889                (mediaTotalNumber.equals(other.mediaTotalNumber)) &&
890                (genre.equals(other.genre)) &&
891                (playingTimeMs.equals(other.playingTimeMs));
892        }
893
894        public String getString(int attrId) {
895            if (!exists)
896                return new String();
897
898            switch (attrId) {
899                case ATTR_TITLE:
900                    return title;
901                case ATTR_ARTIST_NAME:
902                    return artistName;
903                case ATTR_ALBUM_NAME:
904                    return albumName;
905                case ATTR_MEDIA_NUMBER:
906                    return mediaNumber;
907                case ATTR_MEDIA_TOTAL_NUMBER:
908                    return mediaTotalNumber;
909                case ATTR_GENRE:
910                    return genre;
911                case ATTR_PLAYING_TIME_MS:
912                    return playingTimeMs;
913                default:
914                    return new String();
915            }
916        }
917
918        private String stringOrBlank(String s) {
919            return s == null ? new String() : s;
920        }
921
922        private String longStringOrBlank(Long s) {
923            return s == null ? new String() : s.toString();
924        }
925
926        public String toString() {
927            if (!exists) {
928                return "[MediaAttributes: none]";
929            }
930
931            return "[MediaAttributes: " + title + " - " + albumName + " by "
932                    + artistName + " (" + mediaNumber + "/" + mediaTotalNumber + ") "
933                    + genre + "]";
934        }
935    }
936
937    private void updateMetadata(MediaMetadata data) {
938        MediaAttributes oldAttributes = mMediaAttributes;
939        mMediaAttributes = new MediaAttributes(data);
940        if (data == null) {
941            mSongLengthMs = 0L;
942        } else {
943            mSongLengthMs = data.getLong(MediaMetadata.METADATA_KEY_DURATION);
944        }
945
946        if (!oldAttributes.equals(mMediaAttributes)) {
947            Log.v(TAG, "MediaAttributes Changed to " + mMediaAttributes.toString());
948            mTrackNumber++;
949
950            if (mTrackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
951                mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
952                sendTrackChangedRsp();
953            }
954        } else {
955            Log.v(TAG, "Updated " + mMediaAttributes.toString() + " but no change!");
956        }
957
958        // Update the play state, which sends play state and play position
959        // notifications if needed.
960        if (mMediaController != null) {
961            updatePlaybackState(mMediaController.getPlaybackState());
962        } else {
963            updatePlaybackState(null);
964        }
965    }
966
967    private void getRcFeaturesRequestFromNative(byte[] address, int features) {
968        if (DEBUG) Log.v(TAG, "getRcFeaturesRequestFromNative: address=" + address.toString());
969        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_RC_FEATURES, features, 0,
970                Utils.getAddressStringFromByte(address));
971        mHandler.sendMessage(msg);
972    }
973
974    private void getPlayStatusRequestFromNative(byte[] address) {
975        if (DEBUG) Log.v(TAG, "getPlayStatusRequestFromNative: address" + address.toString());
976        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_PLAY_STATUS);
977        msg.obj = address;
978        mHandler.sendMessage(msg);
979    }
980
981    private void getElementAttrRequestFromNative(byte[] address,byte numAttr, int[] attrs) {
982        if (DEBUG) Log.v(TAG, "getElementAttrRequestFromNative: numAttr=" + numAttr);
983        AvrcpCmd avrcpCmdobj = new AvrcpCmd();
984        AvrcpCmd.ElementAttrCmd elemAttr = avrcpCmdobj.new ElementAttrCmd(address, numAttr, attrs);
985        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_ELEM_ATTRS);
986        msg.obj = elemAttr;
987        mHandler.sendMessage(msg);
988    }
989
990    private void registerNotificationRequestFromNative(byte[] address,int eventId, int param) {
991        if (DEBUG) Log.v(TAG, "registerNotificationRequestFromNative: eventId=" + eventId);
992        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_REGISTER_NOTIFICATION, eventId, param);
993        msg.obj = address;
994        mHandler.sendMessage(msg);
995    }
996
997    private void processRegisterNotification(byte[] address, int eventId, int param) {
998        switch (eventId) {
999            case EVT_PLAY_STATUS_CHANGED:
1000                mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
1001                registerNotificationRspPlayStatusNative(mPlayStatusChangedNT,
1002                        convertPlayStateToPlayStatus(mCurrentPlayState));
1003                break;
1004
1005            case EVT_TRACK_CHANGED:
1006                Log.v(TAG, "Track changed notification enabled");
1007                mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
1008                sendTrackChangedRsp();
1009                break;
1010
1011            case EVT_PLAY_POS_CHANGED:
1012                mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
1013                mPlaybackIntervalMs = (long) param * 1000L;
1014                sendPlayPosNotificationRsp(true);
1015                break;
1016
1017            case EVT_AVBL_PLAYERS_CHANGED:
1018                /* Notify remote available players changed */
1019                if (DEBUG) Log.d (TAG, "sending availablePlayersChanged to remote ");
1020                registerNotificationRspAvalPlayerChangedNative(
1021                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM);
1022                break;
1023
1024            case EVT_ADDR_PLAYER_CHANGED:
1025                /* Notify remote addressed players changed */
1026                if (DEBUG) Log.d (TAG, "sending addressedPlayersChanged to remote ");
1027                registerNotificationRspAddrPlayerChangedNative(
1028                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
1029                        mCurrAddrPlayerID, sUIDCounter);
1030                break;
1031
1032            case EVENT_UIDS_CHANGED:
1033                if (DEBUG) Log.d(TAG, "sending UIDs changed to remote");
1034                registerNotificationRspUIDsChangedNative(
1035                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM, sUIDCounter);
1036                break;
1037
1038            case EVENT_NOW_PLAYING_CONTENT_CHANGED:
1039                if (DEBUG) Log.d(TAG, "sending NowPlayingList changed to remote");
1040                /* send interim response to remote device */
1041                if (!registerNotificationRspNowPlayingChangedNative(
1042                        AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
1043                    Log.e(TAG, "EVENT_NOW_PLAYING_CONTENT_CHANGED: " +
1044                            "registerNotificationRspNowPlayingChangedNative for Interim rsp failed!");
1045                }
1046                break;
1047        }
1048    }
1049
1050    private void handlePassthroughCmdRequestFromNative(byte[] address, int id, int keyState) {
1051        switch (id) {
1052            case BluetoothAvrcp.PASSTHROUGH_ID_REWIND:
1053                rewind(address, keyState);
1054                return;
1055            case BluetoothAvrcp.PASSTHROUGH_ID_FAST_FOR:
1056                fastForward(address, keyState);
1057                return;
1058        }
1059
1060        /* For all other pass through commands other than fast forward and backward
1061         * (like play, pause, next, previous, stop, etc.); sending to current addressed player.
1062         */
1063        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_PASS_THROUGH, id, keyState, address);
1064        mHandler.sendMessage(msg);
1065    }
1066
1067    private void fastForward(byte[] address, int keyState) {
1068        Message msg = mHandler.obtainMessage(MSG_FAST_FORWARD, keyState, 0);
1069        Bundle data = new Bundle();
1070        data.putByteArray("BdAddress" , address);
1071        msg.setData(data);
1072        mHandler.sendMessage(msg);
1073    }
1074
1075    private void rewind(byte[] address, int keyState) {
1076        Message msg = mHandler.obtainMessage(MSG_REWIND, keyState, 0);
1077        Bundle data = new Bundle();
1078        data.putByteArray("BdAddress" , address);
1079        msg.setData(data);
1080        mHandler.sendMessage(msg);
1081    }
1082
1083    private void changePositionBy(long amount) {
1084        long currentPosMs = getPlayPosition();
1085        if (currentPosMs == -1L) return;
1086        long newPosMs = Math.max(0L, currentPosMs + amount);
1087        mMediaController.getTransportControls().seekTo(newPosMs);
1088    }
1089
1090    private int getSkipMultiplier() {
1091        long currentTime = SystemClock.elapsedRealtime();
1092        long multi = (long) Math.pow(2, (currentTime - mSkipStartTime)/SKIP_DOUBLE_INTERVAL);
1093        return (int) Math.min(MAX_MULTIPLIER_VALUE, multi);
1094    }
1095
1096    private void sendTrackChangedRsp() {
1097        // for players which does not support Browse or when no track is currently selected
1098        if (!isBrowseSupported(getCurrentAddrPlayer()) || (mTrackNumber == -1)) {
1099            trackChangeRspForBrowseUnsupported();
1100        } else {
1101            // for players which support browsing
1102            mAddressedMediaPlayer.sendTrackChangeWithId(mTrackChangedNT, mTrackNumber,
1103                    mMediaController);
1104        }
1105    }
1106
1107    private void trackChangeRspForBrowseUnsupported() {
1108        byte[] track = new byte[AvrcpConstants.TRACK_ID_SIZE];
1109        /* track is stored in big endian format */
1110        for (int idx = 0; idx < AvrcpConstants.TRACK_ID_SIZE; ++idx) {
1111            if (mTrackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM &&
1112                    mTrackNumber == -1) {
1113                 /* if no track is currently selected then return 0xFF in interim response */
1114                track[idx] = AvrcpConstants.NO_TRACK_SELECTED;
1115            } else {
1116                /* if Browsing is not supported and a track is selected, then return 0x00 */
1117                track[idx] = AvrcpConstants.TRACK_IS_SELECTED;
1118            }
1119        }
1120        registerNotificationRspTrackChangeNative(mTrackChangedNT, track);
1121    }
1122
1123    private long getPlayPosition() {
1124        if (mCurrentPlayState == null) {
1125            return -1L;
1126        }
1127
1128        if (mCurrentPlayState.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
1129            return -1L;
1130        }
1131
1132        if (isPlayingState(mCurrentPlayState)) {
1133            return SystemClock.elapsedRealtime() - mLastStateUpdate + mCurrentPlayState.getPosition();
1134        }
1135
1136        return mCurrentPlayState.getPosition();
1137    }
1138
1139    private int convertPlayStateToPlayStatus(PlaybackState state) {
1140        int playStatus = PLAYSTATUS_ERROR;
1141        switch (state.getState()) {
1142            case PlaybackState.STATE_PLAYING:
1143            case PlaybackState.STATE_BUFFERING:
1144                playStatus = PLAYSTATUS_PLAYING;
1145                break;
1146
1147            case PlaybackState.STATE_STOPPED:
1148            case PlaybackState.STATE_NONE:
1149                playStatus = PLAYSTATUS_STOPPED;
1150                break;
1151
1152            case PlaybackState.STATE_PAUSED:
1153                playStatus = PLAYSTATUS_PAUSED;
1154                break;
1155
1156            case PlaybackState.STATE_FAST_FORWARDING:
1157            case PlaybackState.STATE_SKIPPING_TO_NEXT:
1158            case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
1159                playStatus = PLAYSTATUS_FWD_SEEK;
1160                break;
1161
1162            case PlaybackState.STATE_REWINDING:
1163            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
1164                playStatus = PLAYSTATUS_REV_SEEK;
1165                break;
1166
1167            case PlaybackState.STATE_ERROR:
1168                playStatus = PLAYSTATUS_ERROR;
1169                break;
1170
1171        }
1172        return playStatus;
1173    }
1174
1175    private boolean isPlayingState(PlaybackState state) {
1176        return (state.getState() == PlaybackState.STATE_PLAYING) ||
1177                (state.getState() == PlaybackState.STATE_BUFFERING);
1178    }
1179
1180    /**
1181     * Sends a play position notification, or schedules one to be
1182     * sent later at an appropriate time. If |requested| is true,
1183     * does both because this was called in reponse to a request from the
1184     * TG.
1185     */
1186    private void sendPlayPosNotificationRsp(boolean requested) {
1187        if (!requested && mPlayPosChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
1188            if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: Not registered or requesting.");
1189            return;
1190        }
1191
1192        long playPositionMs = getPlayPosition();
1193
1194        // mNextPosMs is set to -1 when the previous position was invalid
1195        // so this will be true if the new position is valid & old was invalid.
1196        // mPlayPositionMs is set to -1 when the new position is invalid,
1197        // and the old mPrevPosMs is >= 0 so this is true when the new is invalid
1198        // and the old was valid.
1199        if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: (" + requested + ") "
1200                + mPrevPosMs + " <=? " + playPositionMs + " <=? " + mNextPosMs);
1201        if (requested || ((mLastReportedPosition != playPositionMs) &&
1202                (playPositionMs >= mNextPosMs) || (playPositionMs <= mPrevPosMs))) {
1203            if (!requested) mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
1204            registerNotificationRspPlayPosNative(mPlayPosChangedNT, (int) playPositionMs);
1205            mLastReportedPosition = playPositionMs;
1206            if (playPositionMs != PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
1207                mNextPosMs = playPositionMs + mPlaybackIntervalMs;
1208                mPrevPosMs = playPositionMs - mPlaybackIntervalMs;
1209            } else {
1210                mNextPosMs = -1;
1211                mPrevPosMs = -1;
1212            }
1213        }
1214
1215        mHandler.removeMessages(MSG_PLAY_INTERVAL_TIMEOUT);
1216        if (mPlayPosChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM && isPlayingState(mCurrentPlayState)) {
1217            Message msg = mHandler.obtainMessage(MSG_PLAY_INTERVAL_TIMEOUT);
1218            long delay = mPlaybackIntervalMs;
1219            if (mNextPosMs != -1) {
1220                delay = mNextPosMs - (playPositionMs > 0 ? playPositionMs : 0);
1221            }
1222            if (DEBUG) Log.d(TAG, "PLAY_INTERVAL_TIMEOUT set for " + delay + "ms from now");
1223            mHandler.sendMessageDelayed(msg, delay);
1224        }
1225    }
1226
1227    /**
1228     * This is called from AudioService. It will return whether this device supports abs volume.
1229     * NOT USED AT THE MOMENT.
1230     */
1231    public boolean isAbsoluteVolumeSupported() {
1232        return ((mFeatures & BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
1233    }
1234
1235    /**
1236     * We get this call from AudioService. This will send a message to our handler object,
1237     * requesting our handler to call setVolumeNative()
1238     */
1239    public void adjustVolume(int direction) {
1240        Message msg = mHandler.obtainMessage(MSG_ADJUST_VOLUME, direction, 0);
1241        mHandler.sendMessage(msg);
1242    }
1243
1244    public void setAbsoluteVolume(int volume) {
1245        if (volume == mLocalVolume) {
1246            if (DEBUG) Log.v(TAG, "setAbsoluteVolume is setting same index, ignore "+volume);
1247            return;
1248        }
1249
1250        mHandler.removeMessages(MSG_ADJUST_VOLUME);
1251        Message msg = mHandler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME, volume, 0);
1252        mHandler.sendMessage(msg);
1253    }
1254
1255    /* Called in the native layer as a btrc_callback to return the volume set on the carkit in the
1256     * case when the volume is change locally on the carkit. This notification is not called when
1257     * the volume is changed from the phone.
1258     *
1259     * This method will send a message to our handler to change the local stored volume and notify
1260     * AudioService to update the UI
1261     */
1262    private void volumeChangeRequestFromNative(byte[] address, int volume, int ctype) {
1263        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_VOLUME_CHANGE, volume, ctype);
1264        Bundle data = new Bundle();
1265        data.putByteArray("BdAddress" , address);
1266        msg.setData(data);
1267        mHandler.sendMessage(msg);
1268    }
1269
1270    private void getFolderItemsRequestFromNative(byte[] address, byte scope, int startItem, int endItem,
1271            byte numAttr, int[] attrIds) {
1272        if (DEBUG) Log.v(TAG, "getFolderItemsRequestFromNative: scope=" + scope + ", numAttr=" + numAttr);
1273        AvrcpCmd avrcpCmdobj = new AvrcpCmd();
1274        AvrcpCmd.FolderItemsCmd folderObj = avrcpCmdobj.new FolderItemsCmd(address, scope,
1275                startItem, endItem, numAttr, attrIds);
1276        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_FOLDER_ITEMS, 0, 0);
1277        msg.obj = folderObj;
1278        mHandler.sendMessage(msg);
1279    }
1280
1281    private void setAddressedPlayerRequestFromNative(byte[] address, int playerId) {
1282        if (DEBUG) Log.v(TAG, "setAddrPlayerRequestFromNative: playerId=" + playerId);
1283        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_SET_ADDR_PLAYER, playerId, 0);
1284        msg.obj = address;
1285        mHandler.sendMessage(msg);
1286    }
1287
1288    private void setBrowsedPlayerRequestFromNative(byte[] address, int playerId) {
1289        if (DEBUG) Log.v(TAG, "setBrPlayerRequestFromNative: playerId=" + playerId);
1290        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_SET_BR_PLAYER, playerId, 0);
1291        msg.obj = address;
1292        mHandler.sendMessage(msg);
1293    }
1294
1295    private void changePathRequestFromNative(byte[] address, byte direction, byte[] folderUid) {
1296        if (DEBUG) Log.v(TAG, "changePathRequestFromNative: direction=" + direction);
1297        Bundle data = new Bundle();
1298        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_CHANGE_PATH);
1299        data.putByteArray("BdAddress" , address);
1300        data.putByteArray("folderUid" , folderUid);
1301        data.putByte("direction" , direction);
1302        msg.setData(data);
1303        mHandler.sendMessage(msg);
1304    }
1305
1306    private void getItemAttrRequestFromNative(byte[] address, byte scope, byte[] itemUid, int uidCounter,
1307            byte numAttr, int[] attrs) {
1308        if (DEBUG) Log.v(TAG, "getItemAttrRequestFromNative: scope=" + scope + ", numAttr=" + numAttr);
1309        AvrcpCmd avrcpCmdobj = new AvrcpCmd();
1310        AvrcpCmd.ItemAttrCmd itemAttr = avrcpCmdobj.new ItemAttrCmd(address, scope,
1311                itemUid, uidCounter, numAttr, attrs);
1312        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_ITEM_ATTR);
1313        msg.obj = itemAttr;
1314        mHandler.sendMessage(msg);
1315    }
1316
1317    private void searchRequestFromNative(byte[] address, int charsetId, byte[] searchStr) {
1318        if (DEBUG) Log.v(TAG, "searchRequestFromNative");
1319        /* Search is not supported */
1320        if (DEBUG) Log.d(TAG, "search is not supported");
1321        searchRspNative(address, AvrcpConstants.RSP_SRCH_NOT_SPRTD, 0, 0);
1322    }
1323
1324    private void playItemRequestFromNative(byte[] address, byte scope, int uidCounter, byte[] uid) {
1325        if (DEBUG) Log.v(TAG, "playItemRequestFromNative: scope=" + scope);
1326        Bundle data = new Bundle();
1327        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_PLAY_ITEM);
1328        data.putByteArray("BdAddress" , address);
1329        data.putByteArray("uid" , uid);
1330        data.putInt("uidCounter" , uidCounter);
1331        data.putByte("scope" , scope);
1332        msg.setData(data);
1333        mHandler.sendMessage(msg);
1334    }
1335
1336    private void addToPlayListRequestFromNative(byte[] address, byte scope, byte[] uid, int uidCounter) {
1337        if (DEBUG) Log.v(TAG, "addToPlayListRequestFromNative: scope=" + scope);
1338        /* add to NowPlaying not supported */
1339        Log.w(TAG, "Add to NowPlayingList is not supported");
1340        addToNowPlayingRspNative(address, AvrcpConstants.RSP_INTERNAL_ERR);
1341    }
1342
1343    private void getTotalNumOfItemsRequestFromNative(byte[] address, byte scope) {
1344        if (DEBUG) Log.v(TAG, "getTotalNumOfItemsRequestFromNative: scope=" + scope);
1345        Bundle data = new Bundle();
1346        Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_TOTAL_NUM_OF_ITEMS);
1347        msg.arg1 = scope;
1348        msg.obj = address;
1349        mHandler.sendMessage(msg);
1350    }
1351
1352    private void notifyVolumeChanged(int volume) {
1353        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
1354                      AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
1355    }
1356
1357    private int convertToAudioStreamVolume(int volume) {
1358        // Rescale volume to match AudioSystem's volume
1359        return (int) Math.floor((double) volume*mAudioStreamMax/AVRCP_MAX_VOL);
1360    }
1361
1362    private int convertToAvrcpVolume(int volume) {
1363        return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);
1364    }
1365
1366    private void blackListCurrentDevice() {
1367        mFeatures &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
1368        mAudioManager.avrcpSupportsAbsoluteVolume(mAddress, isAbsoluteVolumeSupported());
1369
1370        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1371                Context.MODE_PRIVATE);
1372        SharedPreferences.Editor editor = pref.edit();
1373        editor.putBoolean(mAddress, true);
1374        editor.commit();
1375    }
1376
1377    private int modifyRcFeatureFromBlacklist(int feature, String address) {
1378        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1379                Context.MODE_PRIVATE);
1380        if (!pref.contains(address)) {
1381            return feature;
1382        }
1383        if (pref.getBoolean(address, false)) {
1384            feature &= ~BTRC_FEAT_ABSOLUTE_VOLUME;
1385        }
1386        return feature;
1387    }
1388
1389    public void resetBlackList(String address) {
1390        SharedPreferences pref = mContext.getSharedPreferences(ABSOLUTE_VOLUME_BLACKLIST,
1391                Context.MODE_PRIVATE);
1392        SharedPreferences.Editor editor = pref.edit();
1393        editor.remove(address);
1394        editor.commit();
1395    }
1396
1397    /**
1398     * This is called from A2dpStateMachine to set A2dp audio state.
1399     */
1400    public void setA2dpAudioState(int state) {
1401        Message msg = mHandler.obtainMessage(MSG_SET_A2DP_AUDIO_STATE, state, 0);
1402        mHandler.sendMessage(msg);
1403    }
1404
1405    private class AvrcpServiceBroadcastReceiver extends BroadcastReceiver {
1406        @Override
1407        public void onReceive(Context context, Intent intent) {
1408            String action = intent.getAction();
1409            if (DEBUG) Log.d(TAG, "AvrcpServiceBroadcastReceiver-> Action: " + action);
1410
1411            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1412                    || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
1413                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1414                    // a package is being removed, not replaced
1415                    String packageName = intent.getData().getSchemeSpecificPart();
1416                    if (packageName != null) {
1417                        handlePackageModified(packageName, true);
1418                    }
1419                }
1420
1421            } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
1422                    || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
1423                String packageName = intent.getData().getSchemeSpecificPart();
1424                if (DEBUG) Log.d(TAG,"AvrcpServiceBroadcastReceiver-> packageName: "
1425                        + packageName);
1426                if (packageName != null) {
1427                    handlePackageModified(packageName, false);
1428                }
1429            }
1430        }
1431    }
1432
1433    private void handlePackageModified(String packageName, boolean removed) {
1434        if (DEBUG) Log.d(TAG, "packageName: " + packageName + " removed: " + removed);
1435
1436        if (removed) {
1437            // old package is removed, updating local browsable player's list
1438            if (isBrowseSupported(packageName)) {
1439                removePackageFromBrowseList(packageName);
1440            }
1441        } else {
1442            // new package has been added.
1443            if (isBrowsableListUpdated(packageName)) {
1444                // Rebuilding browsable players list
1445                buildBrowsablePlayersList();
1446            }
1447        }
1448    }
1449
1450    private boolean isBrowsableListUpdated(String newPackageName) {
1451
1452        boolean isUpdated = false;
1453
1454        // getting the browsable media players list from package manager
1455        ArrayList<String> browsePlayersList = new ArrayList<String>();
1456        Intent intent = new Intent("android.media.browse.MediaBrowserService");
1457        List<ResolveInfo> resInfos = mPackageManager.queryIntentServices(intent, 0);
1458        for (ResolveInfo resolveInfo : resInfos) {
1459            browsePlayersList.add(resolveInfo.serviceInfo.packageName);
1460        }
1461
1462        // if new added package is browsable or list has been updated from the global object
1463        if (browsePlayersList.contains(newPackageName)
1464                || browsePlayersList.size() != getBrowsePlayersListSize()) {
1465            isUpdated = true;
1466        }
1467        if (DEBUG) Log.d(TAG, "isBrowsableListUpdated " + newPackageName +
1468                " isUpdated:" +  isUpdated);
1469        return isUpdated;
1470    }
1471
1472    private synchronized void removePackageFromBrowseList(String packageName) {
1473        if (DEBUG) Log.d(TAG, "removePackageFromBrowseList: " + packageName);
1474        int browseInfoID = getBrowseId(packageName);
1475        if (browseInfoID != -1) {
1476            mBrowsePlayerInfoList.remove(browseInfoID);
1477        }
1478    }
1479
1480    /*
1481     * utility function to get the browse player index from global browsable
1482     * list. It may return -1 if specified package name is not in the list.
1483     */
1484    private synchronized int getBrowseId(String packageName) {
1485
1486        boolean response = false;
1487        int browseInfoID = 0;
1488
1489        for (BrowsePlayerInfo info : mBrowsePlayerInfoList) {
1490            if (info.packageName.equals(packageName)) {
1491                response = true;
1492                break;
1493            }
1494            browseInfoID++;
1495        }
1496
1497        if (!response) {
1498            browseInfoID = -1;
1499        }
1500
1501        if (DEBUG) Log.d(TAG, "getBrowseId for packageName: " + packageName +
1502                " , browseInfoID: " + browseInfoID);
1503        return browseInfoID;
1504    }
1505
1506    private void setAddressedPlayer(byte[] bdaddr, int selectedId) {
1507        int status = AvrcpConstants.RSP_NO_ERROR;
1508
1509        if (isCurrentMediaPlayerListEmpty()) {
1510            status = AvrcpConstants.RSP_NO_AVBL_PLAY;
1511            Log.w(TAG, " No Available Players to set, sending response back ");
1512        } else if (!isIdValid(selectedId)) {
1513            status = AvrcpConstants.RSP_INV_PLAYER;
1514            Log.w(TAG, " Invalid Player id: " + selectedId + " to set, sending response back ");
1515        } else if (!isPlayerAlreadyAddressed(selectedId)) {
1516            // register new Media Controller Callback and update the current Ids
1517            if (!updateCurrentController(selectedId, mCurrBrowsePlayerID)) {
1518                status = AvrcpConstants.RSP_INTERNAL_ERR;
1519                Log.e(TAG, "register for new Address player failed: " + mCurrAddrPlayerID);
1520            }
1521        } else {
1522            Log.i(TAG, "requested addressPlayer is already focused:" + getCurrentAddrPlayer());
1523        }
1524
1525        if (DEBUG) Log.d(TAG, "setAddressedPlayer for selectedId: " + selectedId +
1526                " , status: " + status);
1527        // Sending address player response to remote
1528        setAddressedPlayerRspNative(bdaddr, status);
1529    }
1530
1531    private void setBrowsedPlayer(byte[] bdaddr, int selectedId) {
1532        int status = AvrcpConstants.RSP_NO_ERROR;
1533
1534        // checking for error cases
1535        if (isCurrentMediaPlayerListEmpty()) {
1536            status = AvrcpConstants.RSP_NO_AVBL_PLAY;
1537            Log.w(TAG, " No Available Players to set, sending response back ");
1538        } else {
1539            // update current browse player id and start browsing service
1540            updateNewIds(mCurrAddrPlayerID, selectedId);
1541            String browsedPackage = getPackageName(selectedId);
1542
1543            if (!isPackageNameValid(browsedPackage)) {
1544                Log.w(TAG, " Invalid package for id:" + mCurrBrowsePlayerID);
1545                status = AvrcpConstants.RSP_INV_PLAYER;
1546            } else if (!isBrowseSupported(browsedPackage)) {
1547                Log.w(TAG, "Browse unsupported for id:" + mCurrBrowsePlayerID
1548                        + ", packagename : " + browsedPackage);
1549                status = AvrcpConstants.RSP_PLAY_NOT_BROW;
1550            } else if (!startBrowseService(bdaddr, browsedPackage)) {
1551                Log.e(TAG, "service cannot be started for browse player id:" + mCurrBrowsePlayerID
1552                        + ", packagename : " + browsedPackage);
1553                status = AvrcpConstants.RSP_INTERNAL_ERR;
1554            }
1555        }
1556
1557        if (status != AvrcpConstants.RSP_NO_ERROR) {
1558            setBrowsedPlayerRspNative(bdaddr, status, (byte) 0x00, 0, null);
1559        }
1560
1561        if (DEBUG) Log.d(TAG, "setBrowsedPlayer for selectedId: " + selectedId +
1562                " , status: " + status);
1563    }
1564
1565    private MediaSessionManager.OnActiveSessionsChangedListener mActiveSessionListener =
1566            new MediaSessionManager.OnActiveSessionsChangedListener() {
1567
1568        @Override
1569        public void onActiveSessionsChanged(List<MediaController> mediaControllerList) {
1570            if (DEBUG) Log.v(TAG, "received onActiveSessionsChanged");
1571
1572            if (isAvailablePlayersChanged(mediaControllerList)) {
1573                // rebuild the list cached locally in this file
1574                buildMediaPlayersList();
1575
1576                // inform the remote device that the player list has changed
1577                sendAvailablePlayersChanged();
1578            } else if (isAddressedPlayerChanged(mediaControllerList)) {
1579                int newAddrPlayerID = getNewAddrPlayerID(mediaControllerList.get(0)
1580                        .getPackageName());
1581                // inform the remote device that the addressed player has changed
1582                sendAddressedPlayerChanged(newAddrPlayerID);
1583
1584                if (!updateCurrentController(newAddrPlayerID, mCurrBrowsePlayerID)) {
1585                    Log.e(TAG, "register for new Address player failed. id: " + newAddrPlayerID);
1586                }
1587            } else {
1588                if (DEBUG) Log.d(TAG, "Active sessions same, ignoring onActiveSessionsChanged.");
1589            }
1590        }
1591
1592        private void sendAddressedPlayerChanged(int newAddrPlayerID) {
1593            if (DEBUG) Log.d(TAG, "sendAddressedPlayerChanged: new PlayerID=" + newAddrPlayerID);
1594
1595            /* notify remote addressed player changed */
1596            registerNotificationRspAddrPlayerChangedNative(
1597                    AvrcpConstants.NOTIFICATION_TYPE_CHANGED, newAddrPlayerID, sUIDCounter);
1598        }
1599
1600        private void sendAvailablePlayersChanged() {
1601            if (DEBUG) Log.d(TAG, "sendAvailablePlayersChanged");
1602
1603            /* Notify remote available players changed */
1604            registerNotificationRspAvalPlayerChangedNative(
1605                    AvrcpConstants.NOTIFICATION_TYPE_CHANGED);
1606        }
1607
1608        private boolean isAddressedPlayerChanged(List<MediaController> mediaControllerList) {
1609            boolean isAddrPlayerChanged = false;
1610
1611            // checking top of the controller's list with current addressed player
1612            if (mediaControllerList != null && !mediaControllerList.isEmpty()) {
1613                if (!mediaControllerList.get(0).getPackageName().equals(getCurrentAddrPlayer())) {
1614                    isAddrPlayerChanged = true;
1615                }
1616            }
1617
1618            if (DEBUG) Log.d(TAG, "isAddressedPlayerChanged: " + isAddrPlayerChanged);
1619            return isAddrPlayerChanged;
1620        }
1621
1622        private boolean isAvailablePlayersChanged(List<MediaController> mediaControllerList) {
1623            boolean isListModified = false;
1624
1625            /* comparing media controller list from framework and from local cached list */
1626            if (mediaControllerList == null && isCurrentMediaPlayerListEmpty()) {
1627                if (DEBUG) Log.d(TAG,
1628                        "both player list, received from framework and local are empty");
1629                return isListModified;
1630            }
1631
1632            if (mediaControllerList == null && !isCurrentMediaPlayerListEmpty()) {
1633                isListModified = true;
1634                if (DEBUG) Log.d(TAG, "players list is empty, local player list is not empty");
1635            } else if (isCurrentMediaPlayerListEmpty() && mediaControllerList != null) {
1636                if (DEBUG) Log.d(TAG, "players list is not empty, but local player list is empty");
1637                isListModified = true;
1638            } else if (isCtrlListChanged(mediaControllerList, mMPLObj.mControllersList)) {
1639                isListModified = true;
1640            }
1641
1642            if (DEBUG) Log.d(TAG, "isAvailablePlayersChanged: " + isListModified);
1643            return isListModified;
1644        }
1645
1646        private int getNewAddrPlayerID(String newAddressedPlayer) {
1647            int newAddrPlayerId = -1;
1648
1649            for (int id = 0; id < mMPLObj.mPackageNameList.length; id++) {
1650                if (mMPLObj.mPackageNameList[id].equals(newAddressedPlayer)) {
1651                    // increment Id by one, because list Ids starts from 1.
1652                    newAddrPlayerId = id + 1;
1653                    break;
1654                }
1655            }
1656
1657            if (DEBUG) Log.d(TAG, "getNewAddrPlayerID: " + newAddrPlayerId);
1658            return newAddrPlayerId;
1659        }
1660
1661        private boolean isCtrlListChanged(List<MediaController> mediaControllerList,
1662                List<MediaController> mControllersList) {
1663            boolean isListChanged = false;
1664
1665            if (mControllersList.size() != mediaControllerList.size()) {
1666                if (DEBUG) Log.d(TAG, "size of new list and old list are different");
1667                isListChanged = true;
1668            } else {
1669                // loop through both the list and check if any new entry found
1670                for (MediaController newCtrller : mediaControllerList) {
1671                    boolean isPackageExist = false;
1672
1673                    for (MediaController oldCtrller : mControllersList) {
1674                        if (oldCtrller.getPackageName().equals(newCtrller.getPackageName())) {
1675                            isPackageExist = true;
1676                            break;
1677                        }
1678                    }
1679
1680                    if (!isPackageExist) {
1681                        if (DEBUG) Log.d(TAG, "no match found for " + newCtrller.getPackageName());
1682                        isListChanged =  true;
1683                        break;
1684                    }
1685                }
1686            }
1687
1688            if (DEBUG) Log.d(TAG, "isCtrlListChanged: " + isListChanged);
1689            return isListChanged;
1690        }
1691
1692    };
1693
1694    private boolean startBrowseService(byte[] bdaddr, String packageName) {
1695        boolean status = true;
1696
1697        /* creating new instance for Browse Media Player */
1698        String browseService = getBrowseServiceName(packageName);
1699        if (!browseService.isEmpty()) {
1700            mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).setBrowsed(
1701                    packageName, browseService);
1702        } else {
1703            Log.w(TAG, "No Browser service available for " + packageName);
1704            status = false;
1705        }
1706
1707        if (DEBUG) Log.d(TAG, "startBrowseService for packageName: " + packageName +
1708                ", status = " + status);
1709        return status;
1710    }
1711
1712    private synchronized String getBrowseServiceName(String packageName) {
1713        String browseServiceName = "";
1714
1715        // getting the browse service name from browse player info
1716        int browseInfoID = getBrowseId(packageName);
1717        if (browseInfoID != -1) {
1718            browseServiceName = mBrowsePlayerInfoList.get(browseInfoID).serviceClass;
1719        }
1720
1721        if (DEBUG) Log.d(TAG, "getBrowseServiceName for packageName: " + packageName +
1722                ", browseServiceName = " + browseServiceName);
1723        return browseServiceName;
1724    }
1725
1726    /*
1727     * utility function to build list of browsable players identified from
1728     * browse service implementation.
1729     */
1730    private synchronized void buildBrowsablePlayersList() {
1731        if (DEBUG) Log.i(TAG, "buildBrowsablePlayersList()");
1732
1733        // Clearing old browsable player's list
1734        mBrowsePlayerInfoList.clear();
1735
1736        Intent intent = new Intent(android.service.media.MediaBrowserService.SERVICE_INTERFACE);
1737        List<ResolveInfo> resInfos = mPackageManager.queryIntentServices(intent, 0);
1738
1739        for (ResolveInfo resolveInfo : resInfos) {
1740            String displayableName = resolveInfo.loadLabel(mPackageManager).toString();
1741            String serviceName = resolveInfo.serviceInfo.name;
1742            String packageName = resolveInfo.serviceInfo.packageName;
1743
1744            BrowsePlayerInfo infoObj = new BrowsePlayerInfo(packageName, displayableName,
1745                    serviceName);
1746            if (DEBUG)
1747                Log.d(TAG, infoObj.toString());
1748            mBrowsePlayerInfoList.add(infoObj);
1749        }
1750
1751        if (DEBUG) Log.i(TAG, "buildBrowsablePlayersList: found " + resInfos.size() + " players");
1752    }
1753
1754    /* initializing media player info list and prepare media player response object */
1755    private void buildMediaPlayersList() {
1756
1757        initMediaPlayersInfoList();
1758        mMPLObj = prepareMediaPlayerRspObj();
1759
1760        if (mMPLObj.mNumItems > 0) {
1761            // Setting player which is on the Top (id=1) of the list as an Addressed player
1762            updateCurrentController(1, -1);
1763        } else {
1764            Log.i(TAG, "No players available in the media players list");
1765            /* If there are no players available in the media players list, meaning none of the
1766             * players are yet open, so no active players are in the list. But in this case none
1767             * of the AVRCP player related commands can be satisfied. So, launching first available
1768             * browsable player service to avail atleast one player to do AVRCP operations. */
1769            /* Starting media player service */
1770            if ((mBrowsePlayerInfoList != null) && (mBrowsePlayerInfoList.size()!=0)) {
1771                BrowsePlayerInfo player = mBrowsePlayerInfoList.get(0);
1772                Intent intent = new Intent();
1773                intent.setComponent(new ComponentName(player.packageName, player.serviceClass));
1774                Log.i(TAG, "Starting service:" + player.packageName + ", " + player.serviceClass);
1775                mContext.startService(intent);
1776            } else {
1777                Log.e(TAG, "Opening player to support AVRCP operations failed, " +
1778                        "No browsable players available!");
1779            }
1780        }
1781
1782    }
1783
1784    /*
1785     * utility function to build list of active media players identified from
1786     * session manager by getting the active sessions
1787     */
1788    private synchronized void initMediaPlayersInfoList() {
1789        if (DEBUG) Log.v(TAG, "initMediaPlayersInfoList");
1790
1791        // Clearing old browsable player's list
1792        mMediaPlayerInfoList.clear();
1793
1794        /* Initializing all media players */
1795        for (MediaController mediaController : getActiveControllersList()) {
1796            initMediaPlayer(mediaController);
1797        }
1798    }
1799
1800    /* Using session manager apis, getting the list of active media controllers */
1801    private List<MediaController> getActiveControllersList() {
1802        List<MediaController> controllersList = new ArrayList<MediaController>();
1803        controllersList = mMediaSessionManager.getActiveSessions(null);
1804        Log.i(TAG, "getActiveControllersList: " + controllersList.size() + " controllers");
1805        return controllersList;
1806    }
1807
1808    /*
1809     * utility function to initialize media players info and add them to global
1810     * media player info list
1811     */
1812    private synchronized void initMediaPlayer(MediaController mediaController) {
1813
1814        String packageName = mediaController.getPackageName();
1815
1816        MediaPlayerInfo mMediaPlayerInfo = new MediaPlayerInfo(packageName,
1817                AvrcpConstants.PLAYER_TYPE_AUDIO, AvrcpConstants.PLAYER_SUBTYPE_NONE,
1818                getPlayBackState(mediaController), getFeatureBitMask(packageName),
1819                getAppLabel(packageName), mediaController);
1820
1821        if (DEBUG) Log.d(TAG, mMediaPlayerInfo.toString());
1822
1823        mMediaPlayerInfoList.add(mMediaPlayerInfo);
1824    }
1825
1826    /*
1827     * utility function to get the playback state of any media player through
1828     * media controller APIs.
1829     */
1830    private byte getPlayBackState(MediaController mediaController) {
1831        PlaybackState pbState = mediaController.getPlaybackState();
1832        byte playStateBytes = PLAYSTATUS_STOPPED;
1833
1834        if (pbState != null) {
1835            playStateBytes = (byte)convertPlayStateToBytes(pbState.getState());
1836            Log.v(TAG, "getPlayBackState: playStateBytes = " + playStateBytes);
1837        } else {
1838            Log.w(TAG, "playState object null, sending playStateBytes = " + playStateBytes);
1839        }
1840
1841        return playStateBytes;
1842    }
1843
1844    /*
1845     * utility function to map framework's play state values to AVRCP spec
1846     * defined play status values
1847     */
1848    private int convertPlayStateToBytes(int playState) {
1849        switch (playState) {
1850            case PlaybackState.STATE_PLAYING:
1851            case PlaybackState.STATE_BUFFERING:
1852                return PLAYSTATUS_PLAYING;
1853
1854            case PlaybackState.STATE_STOPPED:
1855            case PlaybackState.STATE_NONE:
1856            case PlaybackState.STATE_CONNECTING:
1857                return PLAYSTATUS_STOPPED;
1858
1859            case PlaybackState.STATE_PAUSED:
1860                return PLAYSTATUS_PAUSED;
1861
1862            case PlaybackState.STATE_FAST_FORWARDING:
1863            case PlaybackState.STATE_SKIPPING_TO_NEXT:
1864            case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
1865                return PLAYSTATUS_FWD_SEEK;
1866
1867            case PlaybackState.STATE_REWINDING:
1868            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
1869                return PLAYSTATUS_REV_SEEK;
1870
1871            case PlaybackState.STATE_ERROR:
1872            default:
1873                return PLAYSTATUS_ERROR;
1874        }
1875    }
1876
1877    /*
1878     * utility function to get the feature bit mask of any media player through
1879     * package name
1880     */
1881    private short[] getFeatureBitMask(String packageName) {
1882
1883        ArrayList<Short> featureBitsList = new ArrayList<Short>();
1884
1885        /* adding default feature bits */
1886        featureBitsList.add(AvrcpConstants.AVRC_PF_PLAY_BIT_NO);
1887        featureBitsList.add(AvrcpConstants.AVRC_PF_STOP_BIT_NO);
1888        featureBitsList.add(AvrcpConstants.AVRC_PF_PAUSE_BIT_NO);
1889        featureBitsList.add(AvrcpConstants.AVRC_PF_REWIND_BIT_NO);
1890        featureBitsList.add(AvrcpConstants.AVRC_PF_FAST_FWD_BIT_NO);
1891        featureBitsList.add(AvrcpConstants.AVRC_PF_FORWARD_BIT_NO);
1892        featureBitsList.add(AvrcpConstants.AVRC_PF_BACKWARD_BIT_NO);
1893        featureBitsList.add(AvrcpConstants.AVRC_PF_ADV_CTRL_BIT_NO);
1894
1895        /* Add/Modify browse player supported features. */
1896        if (isBrowseSupported(packageName)) {
1897            featureBitsList.add(AvrcpConstants.AVRC_PF_BROWSE_BIT_NO);
1898            featureBitsList.add(AvrcpConstants.AVRC_PF_UID_UNIQUE_BIT_NO);
1899            featureBitsList.add(AvrcpConstants.AVRC_PF_NOW_PLAY_BIT_NO);
1900            featureBitsList.add(AvrcpConstants.AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO);
1901        }
1902
1903        // converting arraylist to array for response
1904        short[] featureBitsArray = new short[featureBitsList.size()];
1905
1906        for (int i = 0; i < featureBitsList.size(); i++) {
1907            featureBitsArray[i] = featureBitsList.get(i).shortValue();
1908        }
1909
1910        return featureBitsArray;
1911    }
1912
1913    /**
1914     * Checks the Package name if it supports Browsing or not.
1915     *
1916     * @param packageName - name of the package to get the Id.
1917     * @return true if it supports browsing, else false.
1918     */
1919    private synchronized boolean isBrowseSupported(String packageName) {
1920        boolean response = false;
1921
1922        /* check if Browsable Player's list contains this package name */
1923        for (BrowsePlayerInfo info : mBrowsePlayerInfoList) {
1924            if (info.packageName.equals(packageName)) {
1925                // TODO: (apanicke) Currently browsing isn't implemented
1926                // properly and causes metadata to break. Fix browsing
1927                // interface and change this to true.
1928                response = false;
1929            }
1930        }
1931
1932        if (DEBUG) Log.v(TAG, "isBrowseSupported for " + packageName + ": " + response);
1933        return response;
1934    }
1935
1936    /* from the global object, getting the current addressed player's package name */
1937    private String getCurrentAddrPlayer() {
1938        String addrPlayerPackage = "";
1939
1940        if (!isCurrentMediaPlayerListEmpty() && isIdValid(mCurrAddrPlayerID)) {
1941            addrPlayerPackage = mMPLObj.mPackageNameList[mCurrAddrPlayerID - 1];
1942            if (DEBUG) Log.v(TAG, "Current Addressed Player's Package: " + addrPlayerPackage);
1943        } else {
1944            Log.w(TAG, "current addressed player is not yet set.");
1945        }
1946        return addrPlayerPackage;
1947    }
1948
1949    private String getPackageName(int id) {
1950        String packageName = "";
1951
1952        if (!isCurrentMediaPlayerListEmpty() && isIdValid(id)) {
1953            packageName = mMPLObj.mPackageNameList[id - 1];
1954            if (DEBUG) Log.v(TAG, "Current Player's Package: " + packageName);
1955        } else {
1956            Log.w(TAG, "Current media player is empty or id is invalid");
1957        }
1958        return packageName;
1959    }
1960
1961    /* from the global object, getting the current browsed player's package name */
1962    private String getCurrentBrowsedPlayer(byte[] bdaddr) {
1963        String browsedPlayerPackage = "";
1964
1965        Map<String, BrowsedMediaPlayer> connList = mAvrcpBrowseManager.getConnList();
1966        String bdaddrStr = new String(bdaddr);
1967        if(connList.containsKey(bdaddrStr)){
1968            browsedPlayerPackage = connList.get(bdaddrStr).getPackageName();
1969        }
1970        if (DEBUG) Log.v(TAG, "getCurrentBrowsedPlayerPackage: " + browsedPlayerPackage);
1971        return browsedPlayerPackage;
1972    }
1973
1974    /*
1975     * utility function to get the media controller from the current addressed
1976     * player id, can return null in error cases
1977     */
1978    private synchronized MediaController getCurrentMediaController() {
1979        MediaController mediaController = null;
1980
1981        if (mMediaPlayerInfoList == null || mMediaPlayerInfoList.isEmpty()) {
1982            Log.w(TAG, " No available players , sending response back ");
1983            return mediaController;
1984        }
1985
1986        if (!isIdValid(mCurrAddrPlayerID)) {
1987            Log.w(TAG, "CurrPlayerID is not yet set:" + mCurrAddrPlayerID + ", PlayerList length="
1988                    + mMediaPlayerInfoList.size() + " , sending response back");
1989            return mediaController;
1990        }
1991
1992        mediaController = mMediaPlayerInfoList.get(mCurrAddrPlayerID - 1).getMediaController();
1993
1994        if (mediaController != null) {
1995            if (DEBUG)
1996                Log.v(TAG, "getCurrentMediaController: " + mediaController.getPackageName());
1997        }
1998
1999        return mediaController;
2000    }
2001
2002    /*
2003     * Utility function to get the Media player info from package name returns
2004     * null if package name not found in media players list
2005     */
2006    private synchronized MediaPlayerInfo getMediaPlayerInfo(String packageName) {
2007        if (DEBUG) Log.v(TAG, "getMediaPlayerInfo: " + packageName);
2008        if (mMediaPlayerInfoList.size() > 0) {
2009            for (MediaPlayerInfo info : mMediaPlayerInfoList) {
2010                if (packageName.equals(info.getPackageName())) {
2011                    if (DEBUG) Log.v(TAG, "Found " + info.getPackageName());
2012                    return info;
2013                }
2014            }
2015        } else {
2016            if (DEBUG) Log.v(TAG, "Media players list empty");
2017        }
2018        return null;
2019    }
2020
2021    /* prepare media list & return the media player list response object */
2022    private synchronized MediaPlayerListRsp prepareMediaPlayerRspObj() {
2023
2024        /* Forming player list -- */
2025        int numPlayers = mMediaPlayerInfoList.size();
2026
2027        byte[] playerTypes = new byte[numPlayers];
2028        int[] playerSubTypes = new int[numPlayers];
2029        String[] displayableNameArray = new String[numPlayers];
2030        String[] packageNameArray = new String[numPlayers];
2031        byte[] playStatusValues = new byte[numPlayers];
2032        short[] featureBitMaskValues = new short[numPlayers
2033                * AvrcpConstants.AVRC_FEATURE_MASK_SIZE];
2034        List<MediaController> mediaControllerList = new ArrayList<MediaController>();
2035
2036        int playerId = 0;
2037        for (MediaPlayerInfo info : mMediaPlayerInfoList) {
2038            playerTypes[playerId] = info.getMajorType();
2039            playerSubTypes[playerId] = info.getSubType();
2040            packageNameArray[playerId] = info.getPackageName();
2041            displayableNameArray[playerId] = info.getDisplayableName();
2042            playStatusValues[playerId] = info.getPlayStatus();
2043            mediaControllerList.add(info.getMediaController());
2044
2045            for (int numBit = 0; numBit < info.getFeatureBitMask().length; numBit++) {
2046                /* gives which octet this belongs to */
2047                byte octet = (byte) (info.getFeatureBitMask()[numBit] / 8);
2048                /* gives the bit position within the octet */
2049                byte bit = (byte) (info.getFeatureBitMask()[numBit] % 8);
2050                featureBitMaskValues[(playerId * AvrcpConstants.AVRC_FEATURE_MASK_SIZE) + octet] |=
2051                        (1 << bit);
2052            }
2053
2054            /* printLogs */
2055            if (DEBUG) {
2056                Log.d(TAG, "\n   +++ Player " + playerId + " +++   ");
2057                Log.d(TAG, "display Name[" + playerId + "]: " + displayableNameArray[playerId]);
2058                Log.d(TAG, "Package Name[" + playerId + "]: " + packageNameArray[playerId]);
2059                Log.d(TAG, "player Types[" + playerId + "]: " + playerTypes[playerId]);
2060                Log.d(TAG, "Play Status Value[" + playerId + "]: " + playStatusValues[playerId]);
2061                Log.d(TAG, "\n");
2062            }
2063
2064            playerId++;
2065        }
2066
2067        if (DEBUG) Log.d(TAG, "prepareMediaPlayerRspObj: numPlayers = " + numPlayers);
2068
2069        return new MediaPlayerListRsp(AvrcpConstants.RSP_NO_ERROR, sUIDCounter,
2070                numPlayers, AvrcpConstants.BTRC_ITEM_PLAYER, playerTypes, playerSubTypes,
2071                playStatusValues, featureBitMaskValues,
2072                displayableNameArray, packageNameArray, mediaControllerList);
2073
2074    }
2075
2076     /* build media player list and send it to remote. */
2077    private void handleMediaPlayerListRsp(AvrcpCmd.FolderItemsCmd folderObj) {
2078        if (folderObj.mStartItem >= mMPLObj.mNumItems) {
2079            Log.i(TAG, "handleMediaPlayerListRsp: start item = " + folderObj.mStartItem +
2080                    ", but available num of items = " + mMPLObj.mNumItems);
2081            mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants.RSP_INV_RANGE,
2082                    (short) 0, (byte) 0, 0, null, null, null, null, null);
2083        } else {
2084            if (DEBUG) Log.d(TAG, "handleMediaPlayerListRsp: num items = " + mMPLObj.mNumItems);
2085            sendFolderItems(mMPLObj, folderObj.mAddress);
2086        }
2087    }
2088
2089    /* unregister to the old controller, update new IDs and register to the new controller */
2090    private boolean updateCurrentController(int addrId, int browseId) {
2091        boolean registerRsp = true;
2092
2093        if (!unregOldMediaControllerCb()) {
2094            Log.d(TAG, "unregisterOldMediaControllerCallback return false");
2095        }
2096
2097        updateNewIds(addrId, browseId);
2098        if (!regNewMediaControllerCb()) {
2099            Log.d(TAG, "registerOldMediaControllerCallback return false");
2100            registerRsp = false;
2101        }
2102
2103        if (DEBUG) Log.d(TAG, "updateCurrentController: registerRsp = " + registerRsp);
2104        return registerRsp;
2105    }
2106
2107    /* get the current media controller and unregister for the media controller callback */
2108    private boolean unregOldMediaControllerCb() {
2109        boolean isUnregistered = false;
2110
2111        // unregistering callback for old media controller.
2112        MediaController oldController = getCurrentMediaController();
2113        if (oldController != null) {
2114            oldController.unregisterCallback(mMediaControllerCb);
2115            isUnregistered = true;
2116        } else {
2117            Log.i(TAG, "old controller is null, addressPlayerId:" + mCurrAddrPlayerID);
2118        }
2119
2120        if (DEBUG) Log.d(TAG, "unregOldMediaControllerCb: isUnregistered = " + isUnregistered);
2121        return isUnregistered;
2122    }
2123
2124    /* get the current media controller and register for the media controller callback */
2125    private boolean regNewMediaControllerCb() {
2126        // registering callback for new media controller.
2127        MediaController newController = getCurrentMediaController();
2128        mMediaController = newController;
2129
2130        String name = (mMediaController == null) ? "null" : mMediaController.getPackageName();
2131        Log.v(TAG, "MediaController changed to " + name);
2132
2133        if (mMediaController == null) {
2134            Log.i(TAG, "new controller is null, addressPlayerId:" + mCurrAddrPlayerID);
2135            updateMetadata(null);
2136            mAddressedMediaPlayer.updateNowPlayingList(null);
2137            return false;
2138        }
2139
2140        mMediaController.registerCallback(mMediaControllerCb, mHandler);
2141        updateMetadata(mMediaController.getMetadata());
2142        mAddressedMediaPlayer.updateNowPlayingList(mMediaController.getQueue());
2143        return true;
2144    }
2145
2146    /* Handle getfolderitems for scope = VFS, Search, NowPlayingList */
2147    private void handleGetFolderItemBrowseResponse(AvrcpCmd.FolderItemsCmd folderObj, byte[] bdaddr) {
2148        int status = AvrcpConstants.RSP_NO_ERROR;
2149
2150        /* Browsed player is already set */
2151        switch (folderObj.mScope) {
2152            case AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM:
2153                if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) {
2154                    mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getFolderItemsVFS(folderObj);
2155                } else {
2156                    /* No browsed player set. Browsed player should be set by CT before performing browse.*/
2157                    Log.e(TAG, "handleGetFolderItemBrowseResponse: mBrowsedMediaPlayer is null");
2158                    status = AvrcpConstants.RSP_INTERNAL_ERR;
2159                }
2160                break;
2161
2162            case AvrcpConstants.BTRC_SCOPE_NOW_PLAYING:
2163                mAddressedMediaPlayer.getFolderItemsNowPlaying(bdaddr, folderObj, mMediaController);
2164                break;
2165
2166            default:
2167                /* invalid scope */
2168                Log.e(TAG, "handleGetFolderItemBrowseResponse:invalid scope");
2169                status = AvrcpConstants.RSP_INV_SCOPE;
2170        }
2171
2172
2173        if (status != AvrcpConstants.RSP_NO_ERROR) {
2174            getFolderItemsRspNative(bdaddr, status, (short) 0, (byte) 0x00, 0, null, null, null,
2175                null, null, null, null, null);
2176        }
2177
2178    }
2179
2180    /* utility function to update the global values of current Addressed and browsed player */
2181    private synchronized void updateNewIds(int addrId, int browseId) {
2182        mCurrAddrPlayerID = addrId;
2183        mCurrBrowsePlayerID = browseId;
2184
2185        if (DEBUG) Log.v(TAG, "Updated CurrentIds: AddrPlayerID:" + mCurrAddrPlayerID + " to "
2186                + addrId + ", BrowsePlayerID:" + mCurrBrowsePlayerID + " to " + browseId);
2187    }
2188
2189    /* Getting the application's displayable name from package name */
2190    private String getAppLabel(String packageName) {
2191        ApplicationInfo appInfo = null;
2192        try {
2193            appInfo = mPackageManager.getApplicationInfo(packageName, 0);
2194        } catch (NameNotFoundException e) {
2195            e.printStackTrace();
2196        }
2197
2198        return (String) (appInfo != null ? mPackageManager
2199                .getApplicationLabel(appInfo) : "Unknown");
2200    }
2201
2202    private void sendFolderItems(MediaPlayerListRsp rspObj, byte[] bdaddr) {
2203        mediaPlayerListRspNative(bdaddr, rspObj.mStatus, rspObj.mUIDCounter, rspObj.itemType,
2204                rspObj.mNumItems, rspObj.mPlayerTypes, rspObj.mPlayerSubTypes,
2205                rspObj.mPlayStatusValues, rspObj.mFeatureBitMaskValues, rspObj.mPlayerNameList);
2206    }
2207
2208    private void handlePlayItemResponse(byte[] bdaddr, byte[] uid, byte scope) {
2209
2210        if(scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
2211            mAddressedMediaPlayer.playItem(bdaddr, uid, scope, mMediaController);
2212        }
2213        else {
2214            if(!isAddrPlayerSameAsBrowsed(bdaddr)) {
2215                Log.w(TAG, "Remote requesting play item on uid which may not be recognized by" +
2216                        "current addressed player");
2217                playItemRspNative(bdaddr, AvrcpConstants.RSP_INV_ITEM);
2218            }
2219
2220            if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) {
2221                mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).playItem(uid, scope);
2222            } else {
2223                Log.e(TAG, "handlePlayItemResponse: Remote requested playitem " +
2224                        "before setbrowsedplayer");
2225                playItemRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
2226            }
2227        }
2228    }
2229
2230    private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) {
2231        if(itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
2232            mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController);
2233        }
2234        else {
2235            if (mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress) != null)
2236                mAvrcpBrowseManager.getBrowsedMediaPlayer(itemAttr.mAddress).getItemAttr(itemAttr);
2237            else {
2238                Log.e(TAG, "Could not get attributes. mBrowsedMediaPlayer is null");
2239                getItemAttrRspNative(itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR,
2240                        (byte) 0, null, null);
2241            }
2242        }
2243    }
2244
2245    private void handleGetTotalNumOfItemsResponse(byte[] bdaddr, byte scope) {
2246        // for scope as media player list
2247        if (scope == AvrcpConstants.BTRC_SCOPE_PLAYER_LIST) {
2248            int numPlayers = getPlayerListSize();
2249            if (DEBUG) Log.d(TAG, "handleGetTotalNumOfItemsResponse: sending total " + numPlayers +
2250                    " media players.");
2251            getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0,
2252                    numPlayers);
2253        } else if(scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
2254            mAddressedMediaPlayer.getTotalNumOfItems(bdaddr, scope, mMediaController);
2255        } else {
2256            // for FileSystem browsing scopes as VFS, Now Playing
2257            if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) {
2258                mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getTotalNumOfItems(scope);
2259            } else {
2260                Log.e(TAG, "Could not get Total NumOfItems. mBrowsedMediaPlayer is null");
2261                getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, 0, 0);
2262            }
2263        }
2264
2265    }
2266
2267    private synchronized int getPlayerListSize() {
2268        return mMediaPlayerInfoList.size();
2269    }
2270
2271    private synchronized int getBrowsePlayersListSize() {
2272        return mBrowsePlayerInfoList.size();
2273    }
2274
2275    /* check if browsed player and addressed player are same */
2276    private boolean isAddrPlayerSameAsBrowsed(byte[] bdaddr) {
2277        boolean isSame = true;
2278        String browsedPlayer = getCurrentBrowsedPlayer(bdaddr);
2279        String addressedPlayer = getCurrentAddrPlayer();
2280
2281        if (!isPackageNameValid(browsedPlayer)) {
2282            Log.w(TAG, "Browsed player name empty");
2283            isSame = false;
2284        } else if (!addressedPlayer.equals(browsedPlayer)) {
2285            Log.w(TAG, browsedPlayer + " is not current Addressed Player : "
2286                    + addressedPlayer);
2287            isSame = false;
2288        }
2289
2290        if (DEBUG) Log.d(TAG, "isAddrPlayerSameAsBrowsed: isSame = " + isSame);
2291        return isSame;
2292    }
2293
2294    /* checks if global object containing media player list is empty */
2295    private boolean isCurrentMediaPlayerListEmpty() {
2296        boolean isEmpty = (mMPLObj == null || mMPLObj.mPackageNameList == null
2297                || mMPLObj.mPackageNameList.length == 0 || mMediaPlayerInfoList.isEmpty());
2298        if (DEBUG) Log.d(TAG, "Current MediaPlayer List Empty.= " + isEmpty);
2299        return isEmpty;
2300    }
2301
2302    /* checks if the id is within the range of global object containing media player list */
2303    private boolean isIdValid(int id) {
2304        boolean isValid = (id > 0 && id <= mMPLObj.mPackageNameList.length);
2305        if (DEBUG) Log.d(TAG, "Id = " + id + "isIdValid = " + isValid);
2306        return isValid;
2307    }
2308
2309    /* checks if package name is not null or empty */
2310    private boolean isPackageNameValid(String browsedPackage) {
2311        boolean isValid = (browsedPackage != null && browsedPackage.length() > 0);
2312        if (DEBUG) Log.d(TAG, "isPackageNameValid: browsedPackage = " + browsedPackage +
2313                "isValid = " + isValid);
2314        return isValid;
2315    }
2316
2317    /* checks if selected addressed player is already addressed */
2318    private boolean isPlayerAlreadyAddressed(int selectedId) {
2319        // checking if selected ID is same as the current addressed player id
2320        boolean isAddressed = (mCurrAddrPlayerID == selectedId);
2321        if (DEBUG) Log.d(TAG, "isPlayerAlreadyAddressed: isAddressed = " + isAddressed);
2322        return isAddressed;
2323    }
2324
2325    public void dump(StringBuilder sb) {
2326        sb.append("AVRCP:\n");
2327        ProfileService.println(sb, "mMediaAttributes: " + mMediaAttributes);
2328        ProfileService.println(sb, "mTransportControlFlags: " + mTransportControlFlags);
2329        ProfileService.println(sb, "mCurrentPlayState: " + mCurrentPlayState);
2330        ProfileService.println(sb, "mLastStateUpdate: " + mLastStateUpdate);
2331        ProfileService.println(sb, "mPlayStatusChangedNT: " + mPlayStatusChangedNT);
2332        ProfileService.println(sb, "mTrackChangedNT: " + mTrackChangedNT);
2333        ProfileService.println(sb, "mTrackNumber: " + mTrackNumber);
2334        ProfileService.println(sb, "mSongLengthMs: " + mSongLengthMs);
2335        ProfileService.println(sb, "mPlaybackIntervalMs: " + mPlaybackIntervalMs);
2336        ProfileService.println(sb, "mPlayPosChangedNT: " + mPlayPosChangedNT);
2337        ProfileService.println(sb, "mNextPosMs: " + mNextPosMs);
2338        ProfileService.println(sb, "mPrevPosMs: " + mPrevPosMs);
2339        ProfileService.println(sb, "mSkipStartTime: " + mSkipStartTime);
2340        ProfileService.println(sb, "mFeatures: " + mFeatures);
2341        ProfileService.println(sb, "mRemoteVolume: " + mRemoteVolume);
2342        ProfileService.println(sb, "mLastRemoteVolume: " + mLastRemoteVolume);
2343        ProfileService.println(sb, "mLastDirection: " + mLastDirection);
2344        ProfileService.println(sb, "mVolumeStep: " + mVolumeStep);
2345        ProfileService.println(sb, "mAudioStreamMax: " + mAudioStreamMax);
2346        ProfileService.println(sb, "mVolCmdAdjustInProgress: " + mVolCmdAdjustInProgress);
2347        ProfileService.println(sb, "mVolCmdSetInProgress: " + mVolCmdSetInProgress);
2348        ProfileService.println(sb, "mAbsVolRetryTimes: " + mAbsVolRetryTimes);
2349        ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
2350        ProfileService.println(sb, "mVolumeMapping: " + mVolumeMapping.toString());
2351        if (mMediaController != null)
2352            ProfileService.println(sb, "mMediaSession pkg: " + mMediaController.getPackageName());
2353    }
2354
2355    public class AvrcpBrowseManager {
2356        Map<String, BrowsedMediaPlayer> connList = new HashMap<String, BrowsedMediaPlayer>();
2357        private AvrcpMediaRspInterface mMediaInterface;
2358        private Context mContext;
2359
2360        public AvrcpBrowseManager(Context context, AvrcpMediaRspInterface mediaInterface) {
2361            mContext = context;
2362            mMediaInterface = mediaInterface;
2363        }
2364
2365        public void cleanup() {
2366            Iterator entries = connList.entrySet().iterator();
2367            while (entries.hasNext()) {
2368                Map.Entry entry = (Map.Entry) entries.next();
2369                BrowsedMediaPlayer browsedMediaPlayer = (BrowsedMediaPlayer) entry.getValue();
2370                if (browsedMediaPlayer != null) {
2371                    browsedMediaPlayer.cleanup();
2372                }
2373            }
2374            // clean up the map
2375            connList.clear();
2376        }
2377
2378        public void cleanupConn(BluetoothDevice device) {
2379            if(null == device)
2380                return;
2381            String bdaddr = new String(hexStringToByteArray(device.getAddress().replace(":","")));
2382            /* check to see remote device performed setBrowsedPlayer */
2383            if(connList.containsKey(bdaddr)) {
2384                BrowsedMediaPlayer browsedMediaPlayer = connList.get(bdaddr);
2385                /* cleanup browsing connection to media player for disconnected remote device */
2386                if(browsedMediaPlayer != null)
2387                    browsedMediaPlayer.cleanup();
2388                /* remove bdaddr of disconnected device */
2389                connList.remove(bdaddr);
2390            }
2391        }
2392
2393        // get the a free media player interface based on the passed bd address
2394        // if the no items is found for the passed media player then it assignes a
2395        // available media player interface
2396        public BrowsedMediaPlayer getBrowsedMediaPlayer(byte[] bdaddr) {
2397            BrowsedMediaPlayer mediaPlayer;
2398            String bdaddrStr = new String(bdaddr);
2399            if(connList.containsKey(bdaddrStr)){
2400                mediaPlayer = connList.get(bdaddrStr);
2401            } else {
2402                mediaPlayer = new BrowsedMediaPlayer(bdaddr, mContext, mMediaInterface);
2403                connList.put(bdaddrStr, mediaPlayer);
2404            }
2405            return mediaPlayer;
2406        }
2407
2408        // clears the details pertaining to passed bdaddres
2409        public boolean clearBrowsedMediaPlayer(byte[] bdaddr) {
2410            String bdaddrStr = new String(bdaddr);
2411            if(connList.containsKey(bdaddrStr)) {
2412                connList.remove(bdaddrStr);
2413                return true;
2414            }
2415            return false;
2416        }
2417
2418        public Map<String, BrowsedMediaPlayer> getConnList() {
2419            return connList;
2420        }
2421
2422        /* Helper function to convert colon separated bdaddr to byte string */
2423        private byte[] hexStringToByteArray(String s) {
2424            int len = s.length();
2425            byte[] data = new byte[len / 2];
2426            for (int i = 0; i < len; i += 2) {
2427                data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
2428                        + Character.digit(s.charAt(i+1), 16));
2429            }
2430            return data;
2431        }
2432    }
2433
2434    /*
2435     * private class which handles responses from AvrcpMediaManager. Maps responses to native
2436     * responses. This class implements the AvrcpMediaRspInterface interface.
2437     */
2438    private class AvrcpMediaRsp implements AvrcpMediaRspInterface {
2439        private static final String TAG = "AvrcpMediaRsp";
2440
2441        public void setAddrPlayerRsp(byte[] address, int rspStatus) {
2442            if (!setAddressedPlayerRspNative(address, rspStatus)) {
2443                Log.e(TAG, "setAddrPlayerRsp failed!");
2444            }
2445        }
2446
2447        public void setBrowsedPlayerRsp(byte[] address, int rspStatus, byte depth, int numItems,
2448                String[] textArray) {
2449            if (!setBrowsedPlayerRspNative(address, rspStatus, depth, numItems, textArray)) {
2450                Log.e(TAG, "setBrowsedPlayerRsp failed!");
2451            }
2452        }
2453
2454        public void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp rspObj) {
2455            if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
2456                if (!mediaPlayerListRspNative(address, rspStatus, sUIDCounter, rspObj.itemType,
2457                        rspObj.mNumItems, rspObj.mPlayerTypes, rspObj.mPlayerSubTypes,
2458                        rspObj.mPlayStatusValues, rspObj.mFeatureBitMaskValues,
2459                        rspObj.mPlayerNameList))
2460                        Log.e(TAG, "mediaPlayerListRsp failed!");
2461            } else {
2462                Log.e(TAG, "mediaPlayerListRsp: rspObj is null");
2463                if (!mediaPlayerListRspNative(address, rspStatus, sUIDCounter,
2464                        (byte)0x00, 0, null, null, null, null, null))
2465                        Log.e(TAG, "mediaPlayerListRsp failed!");
2466            }
2467        }
2468
2469        public void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp rspObj) {
2470            if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
2471                if (!getFolderItemsRspNative(address, rspStatus, sUIDCounter, rspObj.mScope,
2472                        rspObj.mNumItems, rspObj.mFolderTypes, rspObj.mPlayable, rspObj.mItemTypes,
2473                        rspObj.mItemUid, rspObj.mDisplayNames, rspObj.mAttributesNum,
2474                        rspObj.mAttrIds, rspObj.mAttrValues))
2475                    Log.e(TAG, "getFolderItemsRspNative failed!");
2476            } else {
2477                Log.e(TAG, "folderItemsRsp: rspObj is null or rspStatus is error:" + rspStatus);
2478                if (!getFolderItemsRspNative(address, rspStatus, sUIDCounter, (byte) 0x00, 0,
2479                        null, null, null, null, null, null, null, null))
2480                    Log.e(TAG, "getFolderItemsRspNative failed!");
2481            }
2482
2483        }
2484
2485        public void changePathRsp(byte[] address, int rspStatus, int numItems) {
2486            if (!changePathRspNative(address, rspStatus, numItems))
2487                Log.e(TAG, "changePathRspNative failed!");
2488        }
2489
2490        public void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp rspObj) {
2491            if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
2492                if (!getItemAttrRspNative(address, rspStatus, rspObj.mNumAttr,
2493                        rspObj.mAttributesIds, rspObj.mAttributesArray))
2494                    Log.e(TAG, "getItemAttrRspNative failed!");
2495            } else {
2496                Log.e(TAG, "getItemAttrRsp: rspObj is null or rspStatus is error:" + rspStatus);
2497                if (!getItemAttrRspNative(address, rspStatus, (byte) 0x00, null, null))
2498                    Log.e(TAG, "getItemAttrRspNative failed!");
2499            }
2500        }
2501
2502        public void playItemRsp(byte[] address, int rspStatus) {
2503            if (!playItemRspNative(address, rspStatus)) {
2504                Log.e(TAG, "playItemRspNative failed!");
2505            }
2506        }
2507
2508        public void getTotalNumOfItemsRsp(byte[] address, int rspStatus, int uidCounter,
2509                int numItems) {
2510            if (!getTotalNumOfItemsRspNative(address, rspStatus, sUIDCounter, numItems)) {
2511                Log.e(TAG, "getTotalNumOfItemsRspNative failed!");
2512            }
2513        }
2514
2515        public void addrPlayerChangedRsp(byte[] address, int type, int playerId, int uidCounter) {
2516            if (!registerNotificationRspAddrPlayerChangedNative(type, playerId, sUIDCounter)) {
2517                Log.e(TAG, "registerNotificationRspAddrPlayerChangedNative failed!");
2518            }
2519        }
2520
2521        public void avalPlayerChangedRsp(byte[] address, int type) {
2522            if (!registerNotificationRspAvalPlayerChangedNative(type)) {
2523                Log.e(TAG, "registerNotificationRspAvalPlayerChangedNative failed!");
2524            }
2525        }
2526
2527        public void uidsChangedRsp(byte[] address, int type, int uidCounter) {
2528            if (!registerNotificationRspUIDsChangedNative(type, sUIDCounter)) {
2529                Log.e(TAG, "registerNotificationRspUIDsChangedNative failed!");
2530            }
2531        }
2532
2533        public void nowPlayingChangedRsp(int type) {
2534            if (!registerNotificationRspNowPlayingChangedNative(type)) {
2535                Log.e(TAG, "registerNotificationRspNowPlayingChangedNative failed!");
2536            }
2537        }
2538
2539        public void trackChangedRsp(int type, byte[] uid) {
2540            if (!registerNotificationRspTrackChangeNative(type, uid)) {
2541                Log.e(TAG, "registerNotificationRspTrackChangeNative failed!");
2542            }
2543        }
2544    }
2545
2546    /* getters for some private variables */
2547    public AvrcpBrowseManager getAvrcpBrowseManager() {
2548        return mAvrcpBrowseManager;
2549    }
2550
2551    // Do not modify without updating the HAL bt_rc.h files.
2552
2553    // match up with btrc_play_status_t enum of bt_rc.h
2554    final static int PLAYSTATUS_STOPPED = 0;
2555    final static int PLAYSTATUS_PLAYING = 1;
2556    final static int PLAYSTATUS_PAUSED = 2;
2557    final static int PLAYSTATUS_FWD_SEEK = 3;
2558    final static int PLAYSTATUS_REV_SEEK = 4;
2559    final static int PLAYSTATUS_ERROR = 255;
2560
2561    // match up with btrc_media_attr_t enum of bt_rc.h
2562    final static int MEDIA_ATTR_TITLE = 1;
2563    final static int MEDIA_ATTR_ARTIST = 2;
2564    final static int MEDIA_ATTR_ALBUM = 3;
2565    final static int MEDIA_ATTR_TRACK_NUM = 4;
2566    final static int MEDIA_ATTR_NUM_TRACKS = 5;
2567    final static int MEDIA_ATTR_GENRE = 6;
2568    final static int MEDIA_ATTR_PLAYING_TIME = 7;
2569
2570    // match up with btrc_event_id_t enum of bt_rc.h
2571    final static int EVT_PLAY_STATUS_CHANGED = 1;
2572    final static int EVT_TRACK_CHANGED = 2;
2573    final static int EVT_TRACK_REACHED_END = 3;
2574    final static int EVT_TRACK_REACHED_START = 4;
2575    final static int EVT_PLAY_POS_CHANGED = 5;
2576    final static int EVT_BATT_STATUS_CHANGED = 6;
2577    final static int EVT_SYSTEM_STATUS_CHANGED = 7;
2578    final static int EVT_APP_SETTINGS_CHANGED = 8;
2579    final static int EVENT_NOW_PLAYING_CONTENT_CHANGED = 9;
2580    final static int EVT_AVBL_PLAYERS_CHANGED = 0xa;
2581    final static int EVT_ADDR_PLAYER_CHANGED = 0xb;
2582    final static int EVENT_UIDS_CHANGED = 0x0c;
2583
2584    private native static void classInitNative();
2585    private native void initNative();
2586    private native void cleanupNative();
2587    private native boolean getPlayStatusRspNative(byte[] address, int playStatus, int songLen,
2588            int songPos);
2589    private native boolean getElementAttrRspNative(byte[] address, byte numAttr, int[] attrIds,
2590            String[] textArray);
2591    private native boolean registerNotificationRspPlayStatusNative(int type, int playStatus);
2592    private native boolean registerNotificationRspTrackChangeNative(int type, byte[] track);
2593    private native boolean registerNotificationRspPlayPosNative(int type, int playPos);
2594    private native boolean setVolumeNative(int volume);
2595    private native boolean sendPassThroughCommandNative(int keyCode, int keyState);
2596    private native boolean setAddressedPlayerRspNative(byte[] address, int rspStatus);
2597    private native boolean setBrowsedPlayerRspNative(byte[] address, int rspStatus, byte depth,
2598            int numItems, String[] textArray);
2599    private native boolean mediaPlayerListRspNative(byte[] address, int rsStatus, int uidCounter,
2600            byte item_type, int numItems, byte[] PlayerTypes, int[] PlayerSubTypes,
2601            byte[] playStatusValues, short[] FeatureBitMaskValues, String[] textArray);
2602    private native boolean getFolderItemsRspNative(byte[] address, int rspStatus, short uidCounter,
2603            byte scope, int numItems, byte[] folderTypes, byte[] playable, byte[] itemTypes,
2604            byte[] itemUidArray, String[] textArray, int[] AttributesNum, int[] AttributesIds,
2605            String[] attributesArray);
2606    private native boolean changePathRspNative(byte[] address, int rspStatus, int numItems);
2607    private native boolean getItemAttrRspNative(byte[] address, int rspStatus, byte numAttr,
2608            int[] attrIds, String[] textArray);
2609    private native boolean playItemRspNative(byte[] address, int rspStatus);
2610    private native boolean getTotalNumOfItemsRspNative(byte[] address, int rspStatus,
2611            int uidCounter, int numItems);
2612    private native boolean searchRspNative(byte[] address, int rspStatus, int uidCounter,
2613            int numItems);
2614    private native boolean addToNowPlayingRspNative(byte[] address, int rspStatus);
2615    private native boolean registerNotificationRspAddrPlayerChangedNative(int type,
2616        int playerId, int uidCounter);
2617    private native boolean registerNotificationRspAvalPlayerChangedNative(int type);
2618    private native boolean registerNotificationRspUIDsChangedNative(int type, int uidCounter);
2619    private native boolean registerNotificationRspNowPlayingChangedNative(int type);
2620
2621}
2622