199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker/*
299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Copyright 2018 The Android Open Source Project
399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker *
499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Licensed under the Apache License, Version 2.0 (the "License");
599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * you may not use this file except in compliance with the License.
699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * You may obtain a copy of the License at
799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker *
899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker *      http://www.apache.org/licenses/LICENSE-2.0
999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker *
1099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Unless required by applicable law or agreed to in writing, software
1199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * distributed under the License is distributed on an "AS IS" BASIS,
1299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * See the License for the specific language governing permissions and
1499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * limitations under the License.
1599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker */
1699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
1799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerpackage com.android.bluetooth.avrcp;
1899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
198b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panickerimport android.annotation.NonNull;
2099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.BroadcastReceiver;
2199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.ComponentName;
2299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.Context;
2399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.Intent;
2499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.IntentFilter;
2599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.pm.PackageManager;
2699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.content.pm.ResolveInfo;
2799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.media.session.MediaSession;
2899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.media.session.MediaSessionManager;
2999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.media.session.PlaybackState;
3099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.os.Handler;
3199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.os.Looper;
3299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.util.Log;
3399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport android.view.KeyEvent;
3499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
350f5b951d00b5efb447774eabb899872e6cf83ccbtedwangimport com.android.bluetooth.Utils;
360f5b951d00b5efb447774eabb899872e6cf83ccbtedwang
3799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.ArrayList;
3899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.Collections;
3999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.HashMap;
4099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.HashSet;
4199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.List;
4299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.Map;
4399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.regex.Matcher;
4499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerimport java.util.regex.Pattern;
4599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
4699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker/**
4799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * This class is directly responsible of maintaining the list of Browsable Players as well as
4899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * the list of Addressable Players. This variation of the list doesn't actually list all the
4999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * available players for a getAvailableMediaPlayers request. Instead it only reports one media
5099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * player with ID=0 and all the other browsable players are folders in the root of that player.
5199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker *
5299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * Changing the directory to a browsable player will allow you to traverse that player as normal.
5399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * By only having one root player, we never have to send Addressed Player Changed notifications,
5499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * UIDs Changed notifications, or Available Players Changed notifications.
5599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker *
5699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * TODO (apanicke): Add non-browsable players as song items to the root folder. Selecting that
5799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker * player would effectively cause player switch by sending a play command to that player.
5899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker */
5999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panickerpublic class MediaPlayerList {
6099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final String TAG = "NewAvrcpMediaPlayerList";
6199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final boolean DEBUG = true;
6299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    static boolean sTesting = false;
6399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
6499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final String PACKAGE_SCHEME = "package";
6599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final int NO_ACTIVE_PLAYER = 0;
6699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final int BLUETOOTH_PLAYER_ID = 0;
6799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final String BLUETOOTH_PLAYER_NAME = "Bluetooth Player";
6899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
6999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // mediaId's for the now playing list will be in the form of "NowPlayingId[XX]" where [XX]
7099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // is the Queue ID for the requested item.
7199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final String NOW_PLAYING_ID_PATTERN = Util.NOW_PLAYING_PREFIX + "([0-9]*)";
7299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
7399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // mediaId's for folder browsing will be in the form of [XX][mediaid],  where [XX] is a
7499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // two digit representation of the player id and [mediaid] is the original media id as a
7599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // string.
7699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static final String BROWSE_ID_PATTERN = "\\d\\d.*";
7799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
7899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private Context mContext;
7999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private Looper mLooper; // Thread all media player callbacks and timeouts happen on
8099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private PackageManager mPackageManager;
8199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private MediaSessionManager mMediaSessionManager;
8299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
8399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private Map<Integer, MediaPlayerWrapper> mMediaPlayers =
8499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Collections.synchronizedMap(new HashMap<Integer, MediaPlayerWrapper>());
8599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private Map<String, Integer> mMediaPlayerIds =
8699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Collections.synchronizedMap(new HashMap<String, Integer>());
8799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private Map<Integer, BrowsedPlayerWrapper> mBrowsablePlayers =
8899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Collections.synchronizedMap(new HashMap<Integer, BrowsedPlayerWrapper>());
8999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private int mActivePlayerId = NO_ACTIVE_PLAYER;
9099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
910f5b951d00b5efb447774eabb899872e6cf83ccbtedwang    private AvrcpTargetService.ListCallback mCallback;
9299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
9399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    interface MediaUpdateCallback {
9499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        void run(MediaData data);
9599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
9699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
9799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    interface GetPlayerRootCallback {
9899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        void run(int playerId, boolean success, String rootId, int numItems);
9999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
10099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
10199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    interface GetFolderItemsCallback {
10299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        void run(String parentId, List<ListItem> items);
10399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
10499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
1050f5b951d00b5efb447774eabb899872e6cf83ccbtedwang    interface FolderUpdateCallback {
1060f5b951d00b5efb447774eabb899872e6cf83ccbtedwang        void run(boolean availablePlayers, boolean addressedPlayers, boolean uids);
1070f5b951d00b5efb447774eabb899872e6cf83ccbtedwang    }
1080f5b951d00b5efb447774eabb899872e6cf83ccbtedwang
109fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker    MediaPlayerList(Looper looper, Context context) {
110fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker        Log.v(TAG, "Creating MediaPlayerList");
111fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker
11299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mLooper = looper;
11399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mContext = context;
11499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
11599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // Register for intents where available players might have changed
11699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        IntentFilter pkgFilter = new IntentFilter();
11799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
11899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
11999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
12099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
12199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        pkgFilter.addDataScheme(PACKAGE_SCHEME);
12299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        context.registerReceiver(mPackageChangedBroadcastReceiver, pkgFilter);
12399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
12499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaSessionManager =
12599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
12699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaSessionManager.addOnActiveSessionsChangedListener(
12799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                mActiveSessionsChangedListener, null, new Handler(looper));
12899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaSessionManager.setCallback(mButtonDispatchCallback, null);
129fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker    }
130fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker
1310f5b951d00b5efb447774eabb899872e6cf83ccbtedwang    void init(AvrcpTargetService.ListCallback callback) {
132fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker        Log.v(TAG, "Initializing MediaPlayerList");
133fb1594fe803b8b0f7633d04d8a8740eb2d8d4ea7Ajay Panicker        mCallback = callback;
13499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
13599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // Build the list of browsable players and afterwards, build the list of media players
13699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        Intent intent = new Intent(android.service.media.MediaBrowserService.SERVICE_INTERFACE);
13799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        List<ResolveInfo> playerList =
13899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                mContext
13999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    .getApplicationContext()
14099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    .getPackageManager()
14199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    .queryIntentServices(intent, PackageManager.MATCH_ALL);
14299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
14399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        BrowsablePlayerConnector.connectToPlayers(mContext, mLooper, playerList,
14499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                (List<BrowsedPlayerWrapper> players) -> {
14599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                Log.i(TAG, "init: Browsable Player list size is " + players.size());
14699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
147084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker                // Check to see if the list has been cleaned up before this completed
148084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker                if (mMediaSessionManager == null) {
149084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker                    return;
150084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker                }
151084a14dd10e0b7e6826225486c759de246511ce2Ajay Panicker
15299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                for (BrowsedPlayerWrapper wrapper : players) {
15399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    // Generate new id and add the browsable player
15499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    if (!mMediaPlayerIds.containsKey(wrapper.getPackageName())) {
15599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        mMediaPlayerIds.put(wrapper.getPackageName(), mMediaPlayerIds.size() + 1);
15699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    }
15799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
15899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    d("Adding Browser Wrapper for " + wrapper.getPackageName() + " with id "
15999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                            + mMediaPlayerIds.get(wrapper.getPackageName()));
16099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
16199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    mBrowsablePlayers.put(mMediaPlayerIds.get(wrapper.getPackageName()), wrapper);
16299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
16399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    wrapper.getFolderItems(wrapper.getRootId(),
16499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                            (int status, String mediaId, List<ListItem> results) -> {
16599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                                d("Got the contents for: " + mediaId + " : num results="
16699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                                        + results.size());
16799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                            });
16899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
16999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
17099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // Construct the list of current players
17199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                d("Initializing list of current media players");
17299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                List<android.media.session.MediaController> controllers =
17399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        mMediaSessionManager.getActiveSessions(null);
17499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
17599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                for (android.media.session.MediaController controller : controllers) {
17699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    addMediaPlayer(controller);
17799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
17899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
17999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // If there were any active players and we don't already have one due to the Media
18099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // Framework Callbacks then set the highest priority one to active
18199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                if (mActivePlayerId == 0 && mMediaPlayers.size() > 0) setActivePlayer(1);
18299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            });
18399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
18499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
18599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void cleanup() {
18699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mContext.unregisterReceiver(mPackageChangedBroadcastReceiver);
18799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
18899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveSessionsChangedListener);
18999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaSessionManager.setCallback(null, null);
19099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaSessionManager = null;
19199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
19299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaPlayerIds.clear();
19399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
19499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        for (MediaPlayerWrapper player : mMediaPlayers.values()) {
19599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            player.cleanup();
19699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
19799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaPlayers.clear();
19899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
19999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        for (BrowsedPlayerWrapper player : mBrowsablePlayers.values()) {
20099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            player.disconnect();
20199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
20299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mBrowsablePlayers.clear();
20399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
20499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
20599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    int getCurrentPlayerId() {
20699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        return BLUETOOTH_PLAYER_ID;
20799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
20899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
20999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    MediaPlayerWrapper getActivePlayer() {
21099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        return mMediaPlayers.get(mActivePlayerId);
21199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
21299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
21399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
21499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
21599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // In this case the displayed player is the Bluetooth Player, the number of items is equal
21699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // to the number of players. The root ID will always be empty string in this case as well.
21799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void getPlayerRoot(int playerId, GetPlayerRootCallback cb) {
218f499c3d8ca788862ed618f659b152b0bfe7bef47Hansong Zhang        cb.run(playerId, playerId == BLUETOOTH_PLAYER_ID, "", mBrowsablePlayers.size());
21999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
22099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
22199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // Return the "Bluetooth Player" as the only player always
22299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    List<PlayerInfo> getMediaPlayerList() {
22399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        PlayerInfo info = new PlayerInfo();
22499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        info.id = BLUETOOTH_PLAYER_ID;
22599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        info.name = BLUETOOTH_PLAYER_NAME;
22699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        info.browsable = true;
22799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        List<PlayerInfo> ret = new ArrayList<PlayerInfo>();
22899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        ret.add(info);
22999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
23099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        return ret;
23199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
23299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
2338b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker    @NonNull
23499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    String getCurrentMediaId() {
23599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        final MediaPlayerWrapper player = getActivePlayer();
23699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (player == null) return "";
23799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
238eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker        final PlaybackState state = player.getPlaybackState();
2398b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker        final List<Metadata> queue = player.getCurrentQueue();
2408b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker
2418b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker        // Disable the now playing list if the player doesn't have a queue or provide an active
2428b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker        // queue ID that can be used to determine the active song in the queue.
2438b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker        if (state == null
2448b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker                || state.getActiveQueueItemId() == MediaSession.QueueItem.UNKNOWN_ID
2458b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker                || queue.size() == 0) {
2468b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker            d("getCurrentMediaId: No active queue item Id sending empty mediaId: PlaybackState="
2478b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker                     + state);
24899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return "";
24999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
25099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
25199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        return Util.NOW_PLAYING_PREFIX + state.getActiveQueueItemId();
25299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
25399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
2548b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker    @NonNull
25599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    Metadata getCurrentSongInfo() {
25699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        final MediaPlayerWrapper player = getActivePlayer();
25799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (player == null) return Util.empty_data();
25899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
259eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker        return player.getCurrentMetadata();
26099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
26199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
26299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    PlaybackState getCurrentPlayStatus() {
26399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        final MediaPlayerWrapper player = getActivePlayer();
26499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (player == null) return null;
26599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
266eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker        return player.getPlaybackState();
26799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
26899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
2698b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker    @NonNull
27099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    List<Metadata> getNowPlayingList() {
2718b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker        // Only send the current song for the now playing if there is no active song. See
2728b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker        // |getCurrentMediaId()| for reasons why there might be no active song.
2738b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker        if (getCurrentMediaId().equals("")) {
27499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            List<Metadata> ret = new ArrayList<Metadata>();
2758b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker            Metadata data = getCurrentSongInfo();
2768b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker            data.mediaId = "";
2778b9a5a1d92ac99bc5f602957656c23329f84f964Ajay Panicker            ret.add(data);
27899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return ret;
27999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
28099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
281eb0db9f704a6446e3dcc17b1e824cb3a6930a4a7Ajay Panicker        return getActivePlayer().getCurrentQueue();
28299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
28399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
28499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void playItem(int playerId, boolean nowPlaying, String mediaId) {
28599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (nowPlaying) {
28699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            playNowPlayingItem(mediaId);
28799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        } else {
28899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            playFolderItem(mediaId);
28999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
29099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
29199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
29299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private void playNowPlayingItem(String mediaId) {
29399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        d("playNowPlayingItem: mediaId=" + mediaId);
29499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
29599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        Pattern regex = Pattern.compile(NOW_PLAYING_ID_PATTERN);
29699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        Matcher m = regex.matcher(mediaId);
29799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!m.find()) {
29899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            // This should never happen since we control the media ID's reported
29999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.wtf(TAG, "playNowPlayingItem: Couldn't match mediaId to pattern: mediaId="
30099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    + mediaId);
30199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
30299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
30399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        long queueItemId = Long.parseLong(m.group(1));
30499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (getActivePlayer() != null) {
30599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            getActivePlayer().playItemFromQueue(queueItemId);
30699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
30799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
30899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
30999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private void playFolderItem(String mediaId) {
31099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        d("playFolderItem: mediaId=" + mediaId);
31199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
31299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!mediaId.matches(BROWSE_ID_PATTERN)) {
31399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            // This should never happen since we control the media ID's reported
31499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.wtf(TAG, "playFolderItem: mediaId didn't match pattern: mediaId=" + mediaId);
31599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
31699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
31799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        int playerIndex = Integer.parseInt(mediaId.substring(0, 2));
31899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        String itemId = mediaId.substring(2);
31999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
32099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!mBrowsablePlayers.containsKey(playerIndex)) {
32199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            e("playFolderItem: Do not have the a browsable player with ID " + playerIndex);
32299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
32399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
32499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
32599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mBrowsablePlayers.get(playerIndex).playItem(itemId);
32699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
32799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
32899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void getFolderItemsMediaPlayerList(GetFolderItemsCallback cb) {
32999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        d("getFolderItemsMediaPlayerList: Sending Media Player list for root directory");
33099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
33199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        ArrayList<ListItem> playerList = new ArrayList<ListItem>();
33299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        for (BrowsedPlayerWrapper player : mBrowsablePlayers.values()) {
33399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
33499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            String displayName = Util.getDisplayName(mContext, player.getPackageName());
33599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            int id = mMediaPlayerIds.get(player.getPackageName());
33699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
33799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            d("getFolderItemsMediaPlayerList: Adding player " + displayName);
33899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Folder playerFolder = new Folder(String.format("%02d", id), false, displayName);
33999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            playerList.add(new ListItem(playerFolder));
34099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
34199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        cb.run("", playerList);
34299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        return;
34399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
34499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
34599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void getFolderItems(int playerId, String mediaId, GetFolderItemsCallback cb) {
34699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // The playerId is unused since we always assume the remote device is using the
34799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // Bluetooth Player.
34899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        d("getFolderItems(): playerId=" + playerId + ", mediaId=" + mediaId);
34999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
35099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // The device is requesting the content of the root folder. This folder contains a list of
35199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // Browsable Media Players displayed as folders with their contents contained within.
35299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (mediaId.equals("")) {
35399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            getFolderItemsMediaPlayerList(cb);
35499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
35599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
35699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
35799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!mediaId.matches(BROWSE_ID_PATTERN)) {
35899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            // This should never happen since we control the media ID's reported
35999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.wtf(TAG, "getFolderItems: mediaId didn't match pattern: mediaId=" + mediaId);
36099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
36199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
36299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        int playerIndex = Integer.parseInt(mediaId.substring(0, 2));
36399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        String itemId = mediaId.substring(2);
36499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
36599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // TODO (apanicke): Add timeouts for looking up folder items since media browsers don't
36699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // have to respond.
36799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (mBrowsablePlayers.containsKey(playerIndex)) {
36899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            BrowsedPlayerWrapper wrapper = mBrowsablePlayers.get(playerIndex);
36999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            if (itemId.equals("")) {
37099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                Log.i(TAG, "Empty media id, getting the root for "
37199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        + wrapper.getPackageName());
37299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                itemId = wrapper.getRootId();
37399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            }
37499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
37599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            wrapper.getFolderItems(itemId, (status, id, results) -> {
37699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                if (status != BrowsedPlayerWrapper.STATUS_SUCCESS) {
37799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    cb.run(mediaId, new ArrayList<ListItem>());
37899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    return;
37999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
38099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
38199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                String playerPrefix = String.format("%02d", playerIndex);
38299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                for (ListItem item : results) {
38399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    if (item.isFolder) {
38499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        item.folder.mediaId = playerPrefix.concat(item.folder.mediaId);
38599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    } else {
38699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        item.song.mediaId = playerPrefix.concat(item.song.mediaId);
38799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    }
38899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
38999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                cb.run(mediaId, results);
39099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            });
39199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
39299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        } else {
39399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            cb.run(mediaId, new ArrayList<ListItem>());
39499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
39599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
39699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
39799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // Adds the controller to the MediaPlayerList or updates the controller if we already had
39899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // a controller for a package. Returns the new ID of the controller where its added or its
39999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // previous value if it already existed. Returns -1 if the controller passed in is invalid
40099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    int addMediaPlayer(android.media.session.MediaController controller) {
40199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (controller == null) return -1;
40299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
40399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // Each new player has an ID of 1 plus the highest ID. The ID 0 is reserved to signify that
40499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // there is no active player. If we already have a browsable player for the package, reuse
40599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // that key.
40699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        String packageName = controller.getPackageName();
40799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!mMediaPlayerIds.containsKey(packageName)) {
40899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            mMediaPlayerIds.put(packageName, mMediaPlayerIds.size() + 1);
40999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
41099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
41199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        int playerId = mMediaPlayerIds.get(packageName);
41299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
41399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // If we already have a controller for the package, then update it with this new controller
41499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // as the old controller has probably gone stale.
41599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (mMediaPlayers.containsKey(playerId)) {
41699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            d("Already have a controller for the player: " + packageName + ", updating instead");
41799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            MediaPlayerWrapper player = mMediaPlayers.get(playerId);
41899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            player.updateMediaController(MediaControllerFactory.wrap(controller));
41999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
42099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            // If the media controller we updated was the active player check if the media updated
42199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            if (playerId == mActivePlayerId) {
42299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                sendMediaUpdate(getActivePlayer().getCurrentMediaData());
42399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            }
42499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
42599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return playerId;
42699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
42799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
42899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        MediaPlayerWrapper newPlayer = MediaPlayerWrapper.wrap(
42999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                MediaControllerFactory.wrap(controller),
43099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                mLooper);
43199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
43299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        Log.i(TAG, "Adding wrapped media player: " + packageName + " at key: "
43399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                + mMediaPlayerIds.get(controller.getPackageName()));
43499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
43599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaPlayers.put(playerId, newPlayer);
43699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        return playerId;
43799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
43899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
43999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void removeMediaPlayer(int playerId) {
44099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!mMediaPlayers.containsKey(playerId)) {
44199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            e("Trying to remove nonexistent media player: " + playerId);
44299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
44399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
44499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
44599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // If we removed the active player, set no player as active until the Media Framework
44699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // tells us otherwise
44799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (playerId == mActivePlayerId && playerId != NO_ACTIVE_PLAYER) {
44899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            getActivePlayer().unregisterCallback();
44999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            mActivePlayerId = NO_ACTIVE_PLAYER;
45099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
45199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
4529e18c9de74a1882e629ee99a037be46b3ec18bbaHansong Zhang        final MediaPlayerWrapper wrapper = mMediaPlayers.get(playerId);
45399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        d("Removing media player " + wrapper.getPackageName());
45499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaPlayerIds.remove(wrapper.getPackageName());
45599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaPlayers.remove(playerId);
4569e18c9de74a1882e629ee99a037be46b3ec18bbaHansong Zhang        wrapper.cleanup();
45799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
45899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
45999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void setActivePlayer(int playerId) {
46099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!mMediaPlayers.containsKey(playerId)) {
46199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            e("Player doesn't exist in list(): " + playerId);
46299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
46399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
46499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
46599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (playerId == mActivePlayerId) {
46699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.w(TAG, getActivePlayer().getPackageName() + " is already the active player");
46799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
46899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
46999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
47099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (mActivePlayerId != NO_ACTIVE_PLAYER) getActivePlayer().unregisterCallback();
47199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
47299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mActivePlayerId = playerId;
47399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        getActivePlayer().registerCallback(mMediaPlayerCallback);
47499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        Log.i(TAG, "setActivePlayer(): setting player to " + getActivePlayer().getPackageName());
47599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
47699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // Ensure that metadata is synced on the new player
47799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (!getActivePlayer().isMetadataSynced()) {
47899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.w(TAG, "setActivePlayer(): Metadata not synced on new player");
47999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
48099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
48199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
4820f5b951d00b5efb447774eabb899872e6cf83ccbtedwang        if (Utils.isPtsTestMode()) {
4830f5b951d00b5efb447774eabb899872e6cf83ccbtedwang            sendFolderUpdate(true, true, false);
4840f5b951d00b5efb447774eabb899872e6cf83ccbtedwang        }
4850f5b951d00b5efb447774eabb899872e6cf83ccbtedwang
48699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        sendMediaUpdate(getActivePlayer().getCurrentMediaData());
48799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
48899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
4897293a9642b571f3f2592575f7701a26e881c316eAjay Panicker    // TODO (apanicke): Add logging for media key events in dumpsys
4907293a9642b571f3f2592575f7701a26e881c316eAjay Panicker    void sendMediaKeyEvent(int key, boolean pushed) {
4917293a9642b571f3f2592575f7701a26e881c316eAjay Panicker        d("sendMediaKeyEvent: key=" + key + " pushed=" + pushed);
4927293a9642b571f3f2592575f7701a26e881c316eAjay Panicker        int action = pushed ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
49399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        KeyEvent event = new KeyEvent(action, AvrcpPassthrough.toKeyCode(key));
49499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mMediaSessionManager.dispatchMediaKeyEvent(event);
49599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
49699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
4970f5b951d00b5efb447774eabb899872e6cf83ccbtedwang    private void sendFolderUpdate(boolean availablePlayers, boolean addressedPlayers,
4980f5b951d00b5efb447774eabb899872e6cf83ccbtedwang            boolean uids) {
4990f5b951d00b5efb447774eabb899872e6cf83ccbtedwang        d("sendFolderUpdate");
5000f5b951d00b5efb447774eabb899872e6cf83ccbtedwang        if (mCallback == null) {
5010f5b951d00b5efb447774eabb899872e6cf83ccbtedwang            return;
5020f5b951d00b5efb447774eabb899872e6cf83ccbtedwang        }
5030f5b951d00b5efb447774eabb899872e6cf83ccbtedwang
5040f5b951d00b5efb447774eabb899872e6cf83ccbtedwang        mCallback.run(availablePlayers, addressedPlayers, uids);
5050f5b951d00b5efb447774eabb899872e6cf83ccbtedwang    }
5060f5b951d00b5efb447774eabb899872e6cf83ccbtedwang
50799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private void sendMediaUpdate(MediaData data) {
50899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        d("sendMediaUpdate");
50999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (mCallback == null) {
51099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            return;
51199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
51299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
51399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // Always have items in the queue
51499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (data.queue.size() == 0) {
51599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.i(TAG, "sendMediaUpdate: Creating a one item queue for a player with no queue");
51699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            data.queue.add(data.metadata);
51799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
51899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
51999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        mCallback.run(data);
52099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
52199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
52299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private final MediaSessionManager.OnActiveSessionsChangedListener
52399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            mActiveSessionsChangedListener =
52499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            new MediaSessionManager.OnActiveSessionsChangedListener() {
52599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        @Override
52699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        public void onActiveSessionsChanged(
52799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                List<android.media.session.MediaController> newControllers) {
52899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            synchronized (MediaPlayerList.this) {
52999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                Log.v(TAG, "onActiveSessionsChanged: number of controllers: "
53099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        + newControllers.size());
53199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                if (newControllers.size() == 0) return;
53299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
53399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // Apps are allowed to have multiple MediaControllers. If an app does have
53499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // multiple controllers then newControllers contains them in highest
53599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // priority order. Since we only want to keep the highest priority one,
53699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // we keep track of which controllers we updated and skip over ones
53799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                // we've already looked at.
53899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                HashSet<String> addedPackages = new HashSet<String>();
53999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
54099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                for (int i = 0; i < newControllers.size(); i++) {
54199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    Log.d(TAG, "onActiveSessionsChanged: controller: "
54299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                            + newControllers.get(i).getPackageName());
54399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    if (addedPackages.contains(newControllers.get(i).getPackageName())) {
54499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        continue;
54599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    }
54699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
54799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    addedPackages.add(newControllers.get(i).getPackageName());
54899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    addMediaPlayer(newControllers.get(i));
54999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
55099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            }
55199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
55299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    };
55399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
55499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    // TODO (apanicke): Write a test that tests uninstalling the active session
55599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private final BroadcastReceiver mPackageChangedBroadcastReceiver = new BroadcastReceiver() {
55699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        @Override
55799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        public void onReceive(Context context, Intent intent) {
55899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            String action = intent.getAction();
55999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.v(TAG, "mPackageChangedBroadcastReceiver: action: " + action);
56099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
56199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
56299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
56399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) return;
56499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
56599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                String packageName = intent.getData().getSchemeSpecificPart();
566cd53caa864581bfc679c065696fe3506f46d8d31Hansong Zhang                if (packageName != null && mMediaPlayerIds.containsKey(packageName)) {
56799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    removeMediaPlayer(mMediaPlayerIds.get(packageName));
56899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
56999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
57099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
57199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                String packageName = intent.getData().getSchemeSpecificPart();
57299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                if (packageName != null) {
57399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    if (DEBUG) Log.d(TAG, "Name of package changed: " + packageName);
57499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    // TODO (apanicke): Handle either updating or adding the new package.
57599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    // Check if its browsable and send the UIDS changed to update the
57699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    // root folder
57799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
57899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            }
57999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
58099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    };
58199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
58299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private final MediaPlayerWrapper.Callback mMediaPlayerCallback =
58399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            new MediaPlayerWrapper.Callback() {
58499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        @Override
58599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        public void mediaUpdatedCallback(MediaData data) {
58699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            if (data.metadata == null) {
58799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                Log.d(TAG, "mediaUpdatedCallback(): metadata is null");
58899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                return;
58999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            }
59099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
59199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            if (data.state == null) {
59299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                Log.w(TAG, "mediaUpdatedCallback(): Tried to update with null state");
59399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                return;
59499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            }
59599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
59699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            sendMediaUpdate(data);
59799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
59899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    };
59999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
60099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private final MediaSessionManager.Callback mButtonDispatchCallback =
60199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            new MediaSessionManager.Callback() {
60299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                @Override
60399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                public void onMediaKeyEventDispatched(KeyEvent event, MediaSession.Token token) {
60499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    // TODO (apanicke): Add logging for these
60599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
60699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
60799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                @Override
60899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                public void onMediaKeyEventDispatched(KeyEvent event, ComponentName receiver) {
60999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    // TODO (apanicke): Add logging for these
61099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
61199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
61299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                @Override
61399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                public void onAddressedPlayerChanged(MediaSession.Token token) {
61499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    android.media.session.MediaController controller =
61599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                            new android.media.session.MediaController(mContext, token);
61699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
61799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    if (!mMediaPlayerIds.containsKey(controller.getPackageName())) {
61899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        // Since we have a controller, we can try to to recover by adding the
61999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        // player and then setting it as active.
62099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        Log.w(TAG, "onAddressedPlayerChanged(Token): Addressed Player "
62199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                                + "changed to a player we didn't have a session for");
62299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        addMediaPlayer(controller);
62399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    }
62499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
62599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    Log.i(TAG, "onAddressedPlayerChanged: token=" + controller.getPackageName());
62699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    setActivePlayer(mMediaPlayerIds.get(controller.getPackageName()));
62799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
62899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
62999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                @Override
63099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                public void onAddressedPlayerChanged(ComponentName receiver) {
63199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    if (receiver == null) {
63299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        return;
63399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    }
63499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
63599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    if (!mMediaPlayerIds.containsKey(receiver.getPackageName())) {
63699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        e("onAddressedPlayerChanged(Component): Addressed Player "
63799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                                + "changed to a player we don't have a session for");
63899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                        return;
63999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    }
64099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
64199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    Log.i(TAG, "onAddressedPlayerChanged: component=" + receiver.getPackageName());
64299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                    setActivePlayer(mMediaPlayerIds.get(receiver.getPackageName()));
64399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker                }
64499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            };
64599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
64699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
64799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    void dump(StringBuilder sb) {
6485bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker        sb.append("List of MediaControllers: size=" + mMediaPlayers.size() + "\n");
6495bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker        for (int id : mMediaPlayers.keySet()) {
6505bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker            if (id == mActivePlayerId) {
6515bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker                sb.append("<Active> ");
65299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            }
6535bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker            MediaPlayerWrapper player = mMediaPlayers.get(id);
6545bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker            sb.append("  Media Player " + id + ": " + player.getPackageName() + "\n");
6555bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker            sb.append(player.toString().replaceAll("(?m)^", "  "));
6565bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker            sb.append("\n");
65799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
6585bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker
6595bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker        sb.append("List of Browsers: size=" + mBrowsablePlayers.size() + "\n");
66099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        for (BrowsedPlayerWrapper player : mBrowsablePlayers.values()) {
6615bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker            sb.append(player.toString().replaceAll("(?m)^", "  "));
6625bab1e5ebcc02011b4a587c23234f6025a545168Ajay Panicker            sb.append("\n");
66399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
66499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // TODO (apanicke): Add media key events
66599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // TODO (apanicke): Add last sent data
66699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        // TODO (apanicke): Add addressed player history
66799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
66899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
66999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static void e(String message) {
67099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (sTesting) {
67199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.wtfStack(TAG, message);
67299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        } else {
67399a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.e(TAG, message);
67499a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
67599a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
67699a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker
67799a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    private static void d(String message) {
67899a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        if (DEBUG) {
67999a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker            Log.d(TAG, message);
68099a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker        }
68199a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker    }
68299a82a9d6d6b7d86a2bd0aabeef7b193cfe623dbAjay Panicker}
683