MediaBrowserServiceCompat.java revision 7a7ac26ee6d9ff4f5410991fa9cc97d0090ddaef
1e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/*
2e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Copyright (C) 2015 The Android Open Source Project
3e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
4e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Licensed under the Apache License, Version 2.0 (the "License");
5e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * you may not use this file except in compliance with the License.
6e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * You may obtain a copy of the License at
7e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
8e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *      http://www.apache.org/licenses/LICENSE-2.0
9e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
10e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Unless required by applicable law or agreed to in writing, software
11e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * distributed under the License is distributed on an "AS IS" BASIS,
12e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * See the License for the specific language governing permissions and
14e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * limitations under the License.
15e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */
16e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
17e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seopackage android.support.v4.media;
18e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
19e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.app.Service;
20e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.Intent;
21e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.pm.PackageManager;
22e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Binder;
239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Build;
24e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Bundle;
25e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Handler;
26e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.IBinder;
273f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Message;
283f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Messenger;
299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Parcel;
30e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.RemoteException;
3182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport android.support.annotation.IntDef;
32e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.NonNull;
33e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.Nullable;
3482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Limimport android.support.v4.app.BundleCompat;
35e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.media.session.MediaSessionCompat;
36e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.os.ResultReceiver;
37e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.util.ArrayMap;
387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Limimport android.support.v4.util.Pair;
39e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.text.TextUtils;
40e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.util.Log;
41e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
42e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.FileDescriptor;
43e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.PrintWriter;
4482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.lang.annotation.Retention;
4582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.lang.annotation.RetentionPolicy;
469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport java.util.ArrayList;
4782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.util.Collections;
4882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.util.HashMap;
49e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.util.List;
50e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Limimport static android.support.v4.media.MediaBrowserProtocol.*;
5282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
53e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/**
54e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Base class for media browse services.
55e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p>
56e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Media browse services enable applications to browse media content provided by an application
5782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * and ask the application to start playing it. They may also be used to control content that
58096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * is already playing by way of a {@link MediaSessionCompat}.
59e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p>
60e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
61e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * To extend this class, you must declare the service in your manifest file with
62e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * an intent filter with the {@link #SERVICE_INTERFACE} action.
63e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
64e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * For example:
65e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p><pre>
66096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * &lt;service android:name=".MyMediaBrowserServiceCompat"
67e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *          android:label="&#64;string/service_name" >
68e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;intent-filter>
699703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim *         &lt;action android:name="android.media.browse.MediaBrowserService" />
70e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;/intent-filter>
71e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * &lt;/service>
72e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </pre>
73e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */
74096f2531cb790bc1106377d2da344614a3b88d39Jae Seopublic abstract class MediaBrowserServiceCompat extends Service {
757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private static final String TAG = "MBServiceCompat";
767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
77e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private MediaBrowserServiceImpl mImpl;
799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
80e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
81e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The {@link Intent} that must be declared as handled by the service.
82e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
84e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
85e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
86e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * A key for passing the MediaItem to the ResultReceiver in getItem.
87e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
88e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @hide
89e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
90e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final String KEY_MEDIA_ITEM = "media_item";
91e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
9282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001;
9382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
9482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /** @hide */
9582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    @Retention(RetentionPolicy.SOURCE)
9682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED })
9782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private @interface ResultFlags { }
9882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
9982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
1003f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private final ServiceHandler mHandler = new ServiceHandler();
101e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    MediaSessionCompat.Token mSession;
102e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    interface MediaBrowserServiceImpl {
1043f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        void onCreate();
1059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder onBind(Intent intent);
1067a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        void setSessionToken(MediaSessionCompat.Token token);
1079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
1089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl {
1103f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        private Messenger mMessenger;
1119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
1143f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mMessenger = new Messenger(mHandler);
1159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
1199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            if (SERVICE_INTERFACE.equals(intent.getAction())) {
1203f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                return mMessenger.getBinder();
1219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            }
1229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return null;
1239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1247a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim
1257a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        @Override
1267a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void setSessionToken(final MediaSessionCompat.Token token) {
1277a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            mHandler.post(new Runnable() {
1287a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
1297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                public void run() {
1307a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    for (IBinder key : mConnections.keySet()) {
1317a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        ConnectionRecord connection = mConnections.get(key);
1327a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        try {
1337a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            connection.callbacks.onConnect(connection.root.getRootId(), token,
1347a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                    connection.root.getExtras());
1357a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        } catch (RemoteException e) {
1367a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
1377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            mConnections.remove(key);
1387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        }
1397a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    }
1407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
1417a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            });
1427a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
1439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
1449703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1457a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl,
1467a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.ServiceCompatProxy {
1479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        private Object mServiceObj;
1487a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        private Messenger mMessenger;
1499703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1519703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
1527a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi21.createService(
1537a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    MediaBrowserServiceCompat.this, this);
1547a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
1559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1569703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1579703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1589703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
1599703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent);
1609703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
16182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
16282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
1637a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void setSessionToken(MediaSessionCompat.Token token) {
1647a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.setSessionToken(mServiceObj, token.getToken());
16582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
16682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
16782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
1687a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot(
1697a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                String clientPackageName, int clientUid, Bundle rootHints) {
1707a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            Bundle rootExtras = null;
1717a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (rootHints != null && rootHints.getInt(EXTRA_CLIENT_VERSION, 0) != 0) {
1727a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootHints.remove(EXTRA_CLIENT_VERSION);
1737a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                mMessenger = new Messenger(mHandler);
1747a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras = new Bundle();
1757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
1767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
1777a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
1787a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
1797a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    clientPackageName, clientUid, rootHints);
1807a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (root == null) {
1817a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                return null;
1827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
1837a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (rootExtras == null) {
1847a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras = root.getExtras();
1857a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            } else if (root.getExtras() != null) {
1867a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras.putAll(root.getExtras());
1877a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
1887a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return new MediaBrowserServiceCompatApi21.BrowserRoot(
1897a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    root.getRootId(), rootExtras);
19082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
19101b922eddadab50910ce289921001a63855e1f8eSungsoo Lim
19201b922eddadab50910ce289921001a63855e1f8eSungsoo Lim        @Override
1937a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void onLoadChildren(String parentId,
1947a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                final MediaBrowserServiceCompatApi21.ResultWrapper resultWrapper) {
19501b922eddadab50910ce289921001a63855e1f8eSungsoo Lim
1967a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            final Result<List<MediaBrowserCompat.MediaItem>> result
1977a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
1987a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
1997a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flag) {
2007a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    List<Parcel> parcelList = null;
2017a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    if (list != null) {
2027a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        parcelList = new ArrayList<>();
2037a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        for (MediaBrowserCompat.MediaItem item : list) {
2047a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            Parcel parcel = Parcel.obtain();
2057a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            item.writeToParcel(parcel, 0);
2067a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            parcelList.add(parcel);
2077a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        }
2087a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    }
2097a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    resultWrapper.sendResult(parcelList);
2107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
2117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim
2127a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
2137a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                public void detach() {
2147a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    resultWrapper.detach();
2157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
2167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            };
2177a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompat.this.onLoadChildren(parentId, result);
21801b922eddadab50910ce289921001a63855e1f8eSungsoo Lim        }
21901b922eddadab50910ce289921001a63855e1f8eSungsoo Lim    }
22001b922eddadab50910ce289921001a63855e1f8eSungsoo Lim
2213f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private final class ServiceHandler extends Handler {
2227a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        private final ServiceBinderImpl mServiceBinderImpl = new ServiceBinderImpl();
2233f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
2243f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        @Override
2253f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void handleMessage(Message msg) {
226094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
2273f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            switch (msg.what) {
22882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_CONNECT:
2297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.connect(data.getString(DATA_PACKAGE_NAME),
230094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                            data.getInt(DATA_CALLING_UID), data.getBundle(DATA_ROOT_HINTS),
2313f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
2323f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
23382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_DISCONNECT:
2347a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo));
2353f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
23682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_ADD_SUBSCRIPTION:
2377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.addSubscription(data.getString(DATA_MEDIA_ITEM_ID),
2387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
2397a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            data.getBundle(DATA_OPTIONS),
2407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
2413f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
24282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_REMOVE_SUBSCRIPTION:
2437a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.removeSubscription(data.getString(DATA_MEDIA_ITEM_ID),
2447a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
2457a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
2463f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
24782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_GET_MEDIA_ITEM:
2487a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.getMediaItem(data.getString(DATA_MEDIA_ITEM_ID),
249094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                            (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER));
2503f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
25116d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                case CLIENT_MSG_REGISTER_CALLBACK_MESSENGER:
2527a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.registerCallbacks(new ServiceCallbacksCompat(msg.replyTo));
25316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    break;
25499f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                case CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER:
2557a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.unregisterCallbacks(new ServiceCallbacksCompat(msg.replyTo));
25699f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    break;
2573f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                default:
25882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    Log.w(TAG, "Unhandled message: " + msg
25982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Service version: " + SERVICE_VERSION_CURRENT
26082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Client version: " + msg.arg1);
2613f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
2623f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
2633f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
264094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        @Override
265094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
266094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // Binder.getCallingUid() in handleMessage will return the uid of this process.
267094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // In order to get the right calling uid, Binder.getCallingUid() should be called here.
268094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
269d4c131eea07dc2593f70523979929e52cf80e31eSungsoo Lim            data.setClassLoader(MediaBrowserCompat.class.getClassLoader());
270094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putInt(DATA_CALLING_UID, Binder.getCallingUid());
271094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            return super.sendMessageAtTime(msg, uptimeMillis);
272094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        }
273094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim
2743f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void postOrRun(Runnable r) {
2753f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            if (Thread.currentThread() == getLooper().getThread()) {
2763f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                r.run();
2773f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            } else {
2783f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                post(r);
2793f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
2803f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
2813f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
2827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public ServiceBinderImpl getServiceBinderImpl() {
2837a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return mServiceBinderImpl;
2843f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
2853f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    }
2863f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
287e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
288e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * All the info about a connection.
289e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
290e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private class ConnectionRecord {
291e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        String pkg;
292e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Bundle rootHints;
2939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        ServiceCallbacks callbacks;
294e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        BrowserRoot root;
2957a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap();
296e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
297e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
298e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
299096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}.
300e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
301e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Each of the methods that takes one of these to send the result must call
30282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@link #sendResult} to respond to the caller with the given results. If those
303e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * functions return without calling {@link #sendResult}, they must instead call
304e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link #detach} before returning, and then may call {@link #sendResult} when
30582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * they are done. If more than one of those methods is called, an exception will
306e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * be thrown.
307e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
308096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadChildren
309096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadItem
310e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
3119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    public static class Result<T> {
312e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private Object mDebug;
313e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mDetachCalled;
314e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mSendResultCalled;
31582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        private int mFlags;
316e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
317e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Result(Object debug) {
318e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDebug = debug;
319e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
320e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
321e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
322e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Send the result back to the caller.
323e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
324e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void sendResult(T result) {
325e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
326e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
327e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
328e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mSendResultCalled = true;
32982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onResultSent(result, mFlags);
330e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
331e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
332e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
333e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Detach this message from the current thread and allow the {@link #sendResult}
334e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * call to happen later.
335e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
336e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void detach() {
337e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mDetachCalled) {
338e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when detach() had already"
339e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
340e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
341e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
342e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when sendResult() had already"
343e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
344e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
345e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDetachCalled = true;
346e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
347e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
348e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        boolean isDone() {
349e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mDetachCalled || mSendResultCalled;
350e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
351e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
35282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void setFlags(@ResultFlags int flags) {
35382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            mFlags = flags;
35482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
35582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
356e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
357e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Called when the result is sent, after assertions about not being called twice
358e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * have happened.
359e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
36082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void onResultSent(T result, @ResultFlags int flags) {
361e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
362e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
363e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
3647a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private class ServiceBinderImpl {
365094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public void connect(final String pkg, final int uid, final Bundle rootHints,
3669703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                final ServiceCallbacks callbacks) {
367e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
368e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (!isValidPackage(pkg, uid)) {
369e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
370e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " package=" + pkg);
371e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
372e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
3733f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
3749703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
3759703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
3769703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
3779703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
37882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
3799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    mConnections.remove(b);
3809703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
3819703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
3829703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.pkg = pkg;
3839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.rootHints = rootHints;
3849703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.callbacks = callbacks;
3859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
3869703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.root =
3879703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints);
3889703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
3899703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // If they didn't return something, don't allow this client.
3909703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection.root == null) {
3919703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.i(TAG, "No root for client " + pkg + " from service "
3929703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + getClass().getName());
3939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
3949703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            callbacks.onConnectFailed();
3959703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
3969703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
3979703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
3989703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        }
3999703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    } else {
4009703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
4019703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.put(b, connection);
4029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            if (mSession != null) {
4039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                callbacks.onConnect(connection.root.getRootId(),
4049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                        mSession, connection.root.getExtras());
405e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            }
4069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
4079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnect() failed. Dropping client. "
4089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
4099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.remove(b);
410e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        }
411e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
4129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
4139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
414e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
415e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
4169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void disconnect(final ServiceCallbacks callbacks) {
4173f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
4189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
4199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
4209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
4219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
42282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
4239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord old = mConnections.remove(b);
4249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (old != null) {
4259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        // TODO
426e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
4279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
4289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
429e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
430e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
4317a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void addSubscription(final String id, final IBinder token, final Bundle options,
43282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                final ServiceCallbacks callbacks) {
4333f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
4349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
4359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
4369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
4376a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4389703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // Get the record for the connection
4399703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = mConnections.get(b);
4409703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection == null) {
4419703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.w(TAG, "addSubscription for callback that isn't registered id="
4429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + id);
4439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        return;
444493571364635be0190cea8ee230a601070391e6fIan Pedowitz                    }
4459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4467a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    MediaBrowserServiceCompat.this.addSubscription(id, connection, token, options);
4479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
4489703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
449e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
450e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
4517a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void removeSubscription(final String id, final IBinder token,
45282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                final ServiceCallbacks callbacks) {
4533f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
454e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
455e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
456e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    final IBinder b = callbacks.asBinder();
457e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
458e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(b);
459e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (connection == null) {
460e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
461e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + id);
462e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        return;
463e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
46482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    if (!MediaBrowserServiceCompat.this.removeSubscription(
4657a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            id, connection, token)) {
466e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription called for " + id
467e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " which is not subscribed");
468e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
469e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
470e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
471e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
472e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
473e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
474e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (TextUtils.isEmpty(mediaId) || receiver == null) {
475e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return;
476e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
477e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
4783f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
479e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
480e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
481e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    performLoadItem(mediaId, receiver);
482e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
483e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
484e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
48516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim
48616d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
48716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim        public void registerCallbacks(final ServiceCallbacks callbacks) {
48816d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim            mHandler.postOrRun(new Runnable() {
48916d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                @Override
49016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                public void run() {
49116d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    final IBinder b = callbacks.asBinder();
49216d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
49316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    mConnections.remove(b);
49416d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim
49516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
49616d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    connection.callbacks = callbacks;
49716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    mConnections.put(b, connection);
49816d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                }
49916d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim            });
50016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim        }
50199f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim
50299f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
50399f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        public void unregisterCallbacks(final ServiceCallbacks callbacks) {
50499f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim            mHandler.postOrRun(new Runnable() {
50599f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                @Override
50699f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                public void run() {
50799f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    final IBinder b = callbacks.asBinder();
50899f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    mConnections.remove(b);
50999f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                }
51099f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim            });
51199f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        }
512e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
513e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private interface ServiceCallbacks {
5159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder asBinder();
5169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
5179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
5189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnectFailed() throws RemoteException;
51982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list, Bundle options)
5209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
5219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
5229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private class ServiceCallbacksCompat implements ServiceCallbacks {
5243f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        final Messenger mCallbacks;
5259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5263f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        ServiceCallbacksCompat(Messenger callbacks) {
5279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks = callbacks;
5289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5309703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder asBinder() {
5313f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            return mCallbacks.getBinder();
5329703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5339703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
5359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException {
53682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            if (extras == null) {
53782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                extras = new Bundle();
53882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            }
53982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
5403f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Bundle data = new Bundle();
541094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, root);
542094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putParcelable(DATA_MEDIA_SESSION_TOKEN, session);
543094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putBundle(DATA_ROOT_HINTS, extras);
544094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT, data);
5459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnectFailed() throws RemoteException {
548094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null);
5499703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
55182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list,
55282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                Bundle options) throws RemoteException {
553094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = new Bundle();
554094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, mediaId);
55582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            data.putBundle(DATA_OPTIONS, options);
556ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            if (list != null) {
557094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                data.putParcelableArrayList(DATA_MEDIA_ITEM_LIST,
558ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim                        list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
559ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            }
560094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, data);
5613f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
5623f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
563094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        private void sendRequest(int what, Bundle data) throws RemoteException {
5643f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Message msg = Message.obtain();
5653f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.what = what;
56682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            msg.arg1 = SERVICE_VERSION_CURRENT;
5673f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.setData(data);
5683f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mCallbacks.send(msg);
5699703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5709703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
5719703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
572e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
573e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void onCreate() {
574e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        super.onCreate();
5757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (Build.VERSION.SDK_INT >= 21) {
5769703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplApi21();
5779703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        } else {
5789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplBase();
5799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5809703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        mImpl.onCreate();
581e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
582e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
583e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
584e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public IBinder onBind(Intent intent) {
5859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        return mImpl.onBind(intent);
586e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
587e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
588e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
589e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
590e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
591e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
592e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
593e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get the root information for browsing by a particular client.
594e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
595e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The implementation should verify that the client package has permission
596e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * to access browse media information before returning the root id; it
597e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * should return null if the client is not allowed to access this
598e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * information.
599e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * </p>
600e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
601e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientPackageName The package name of the application which is
602e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            requesting access to browse media.
603e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientUid The uid of the application which is requesting access to
604e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            browse media.
605e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param rootHints An optional bundle of service-specific arguments to send
606e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            to the media browse service when connecting and retrieving the
607e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            root id for browsing, or null if none. The contents of this
608e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            bundle may affect the information returned when browsing.
609e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @return The {@link BrowserRoot} for accessing this app's content or null.
61026e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_RECENT
61126e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_OFFLINE
61226e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_SUGGESTED
613e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
614e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
615e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            int clientUid, @Nullable Bundle rootHints);
616e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
617e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
618e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about the children of a media item.
619e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
620e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}
621e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * with the list of children. If loading the children will be an expensive
622e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * operation that should be performed on another thread,
623e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link Result#detach result.detach} may be called before returning from
624e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * this function, and then {@link Result#sendResult result.sendResult}
625e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * called when the loading is complete.
626e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
627e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose children are to be
628e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            queried.
629e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the list of children to, or null if the
630e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            id is invalid.
631e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
632e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract void onLoadChildren(@NonNull String parentId,
633096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result);
634e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
635e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
63682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Called to get information about the children of a media item.
63782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * <p>
63882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Implementations must call {@link Result#sendResult result.sendResult}
63982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * with the list of children. If loading the children will be an expensive
64082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * operation that should be performed on another thread,
64182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@link Result#detach result.detach} may be called before returning from
64282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * this function, and then {@link Result#sendResult result.sendResult}
64382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * called when the loading is complete.
64482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *
64582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param parentId The id of the parent media item whose children are to be
64682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            queried.
64782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param result The Result to send the list of children to, or null if the
64882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            id is invalid.
64982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param options A bundle of service-specific arguments sent from the media
65082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            browse. The information returned through the result should be
65182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            affected by the contents of this bundle.
65282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@hide}
65382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
65482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void onLoadChildren(@NonNull String parentId,
65582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result, @NonNull Bundle options) {
65682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // To support backward compatibility, when the implementation of MediaBrowserService doesn't
65782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // override onLoadChildren() with options, onLoadChildren() without options will be used
65882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // instead, and the options will be applied in the implementation of result.onResultSent().
65982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED);
66082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        onLoadChildren(parentId, result);
66182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
66282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
66382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
664e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about a specific media item.
665e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
666e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}. If
667e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * loading the item will be an expensive operation {@link Result#detach
668e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * result.detach} may be called before returning from this function, and
669e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * then {@link Result#sendResult result.sendResult} called when the item has
670e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * been loaded.
671e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
672e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The default implementation sends a null result.
673e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
674e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param itemId The id for the specific
675096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     *            {@link MediaBrowserCompat.MediaItem}.
676e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the item to, or null if the id is
677e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            invalid.
678e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
679096f2531cb790bc1106377d2da344614a3b88d39Jae Seo    public void onLoadItem(String itemId, Result<MediaBrowserCompat.MediaItem> result) {
680e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        result.sendResult(null);
681e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
682e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
683e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
684e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call to set the media session.
685e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
686e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This should be called as soon as possible during the service's startup.
687e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * It may only be called once.
688e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
689096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @param token The token for the service's {@link MediaSessionCompat}.
690e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
6917a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    public void setSessionToken(MediaSessionCompat.Token token) {
692e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (token == null) {
693e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("Session token may not be null.");
694e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
695e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (mSession != null) {
696e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("The session token has already been set.");
697e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
698e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mSession = token;
6997a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        mImpl.setSessionToken(token);
700e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
701e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
702e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
703e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Gets the session token, or null if it has not yet been created
704e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * or if it has been destroyed.
705e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
706e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public @Nullable MediaSessionCompat.Token getSessionToken() {
707e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return mSession;
708e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
709e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
710e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
711e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Notifies all connected media browsers that the children of
712e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * the specified parent id have changed in some way.
713e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This will cause browsers to fetch subscribed content again.
714e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
715e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose
716e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * children changed.
717e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
71882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId) {
71982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        notifyChildrenChangedInternal(parentId, null);
72082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
72182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
72282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
72382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Notifies all connected media browsers that the children of
72482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * the specified parent id have changed in some way.
72582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * This will cause browsers to fetch subscribed content again.
72682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *
72782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param parentId The id of the parent media item whose
72882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            children changed.
72982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param options A bundle of service-specific arguments to send
73082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            to the media browse. The contents of this bundle may
73182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            contain the information about the change.
73282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@hide}
73382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
73482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
73582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (options == null) {
73682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
73782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
73882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        notifyChildrenChangedInternal(parentId, options);
73982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
74082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
74182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private void notifyChildrenChangedInternal(final String parentId, final Bundle options) {
742e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (parentId == null) {
743e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
744e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
745e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mHandler.post(new Runnable() {
746e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
747e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            public void run() {
748e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                for (IBinder binder : mConnections.keySet()) {
749e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(binder);
7507a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    List<Pair<IBinder, Bundle>> callbackList =
7517a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            connection.subscriptions.get(parentId);
7527a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    if (callbackList != null) {
7537a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        for (Pair<IBinder, Bundle> callback : callbackList) {
7547a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            if (MediaBrowserCompatUtils.hasDuplicatedItems(
7557a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                    options, callback.second)) {
7567a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                performLoadChildren(parentId, connection, callback.second);
75782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                            }
75882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                        }
759e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
760e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
761e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
762e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        });
763e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
764e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
765e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
766e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Return whether the given package is one of the ones that is owned by the uid.
767e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
768e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private boolean isValidPackage(String pkg, int uid) {
769e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (pkg == null) {
770e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return false;
771e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
772e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final PackageManager pm = getPackageManager();
773e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final String[] packages = pm.getPackagesForUid(uid);
774e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final int N = packages.length;
775e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        for (int i=0; i<N; i++) {
776e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (packages[i].equals(pkg)) {
777e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return true;
778e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
779e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
780e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return false;
781e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
782e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
783e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
784e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Save the subscription and if it is a new subscription send the results.
785e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
7867a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private void addSubscription(String id, ConnectionRecord connection, IBinder token,
7877a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            Bundle options) {
788e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // Save the subscription
7897a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
7907a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (callbackList == null) {
7917a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            callbackList = new ArrayList<>();
79282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
7937a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        for (Pair<IBinder, Bundle> callback : callbackList) {
7947a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (token == callback.first
7957a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    && MediaBrowserCompatUtils.areSameOptions(options, callback.second)) {
79682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                return;
79782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
79882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
7997a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        callbackList.add(new Pair<>(token, options));
8007a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        connection.subscriptions.put(id, callbackList);
801e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // send the results
80282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        performLoadChildren(id, connection, options);
80382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
80482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
80582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
80682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Remove the subscription.
80782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
8087a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
8097a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (token == null) {
8107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return connection.subscriptions.remove(id) != null;
8117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
81282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        boolean removed = false;
8137a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
8147a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (callbackList != null) {
8157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            for (Pair<IBinder, Bundle> callback : callbackList) {
8167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                if (token == callback.first) {
81782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    removed = true;
8187a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    callbackList.remove(callback);
81982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                }
82082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
8217a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (callbackList.size() == 0) {
82282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                connection.subscriptions.remove(id);
82382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
82482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
82582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        return removed;
826e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
827e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
828e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
829e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call onLoadChildren and then send the results back to the connection.
830e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
831e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Callers must make sure that this connection is still connected.
832e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
83382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private void performLoadChildren(final String parentId, final ConnectionRecord connection,
83482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            final Bundle options) {
835096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<List<MediaBrowserCompat.MediaItem>> result
836096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
837e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
83882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flag) {
839e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
8407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    if (DEBUG) {
841e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
842e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
843e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
844e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    return;
845e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
846e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
84782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                List<MediaBrowserCompat.MediaItem> filteredList =
84882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                        (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
8497a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                ? applyOptions(list, options) : list;
850e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                try {
85182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    connection.callbacks.onLoadChildren(parentId, filteredList, options);
852e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                } catch (RemoteException ex) {
853e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    // The other side is in the process of crashing.
854e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
855e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            + " package=" + connection.pkg);
856e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
857e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
858e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        };
859e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
86082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (options == null) {
86182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onLoadChildren(parentId, result);
86282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        } else {
86382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onLoadChildren(parentId, result, options);
86482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
865e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
866e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
867e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
868e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for package=" + connection.pkg + " id=" + parentId);
869e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
870e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
871e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
87282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private List<MediaBrowserCompat.MediaItem> applyOptions(List<MediaBrowserCompat.MediaItem> list,
87382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            final Bundle options) {
8747a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (list == null) {
8757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return null;
8767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
87782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int page = options.getInt(MediaBrowserCompat.EXTRA_PAGE, -1);
87882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int pageSize = options.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1);
87982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (page == -1 && pageSize == -1) {
88082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            return list;
88182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
8827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        int fromIndex = pageSize * page;
88382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int toIndex = fromIndex + pageSize;
8847a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
8857a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return Collections.EMPTY_LIST;
88682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
88782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (toIndex > list.size()) {
88882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            toIndex = list.size();
88982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
89082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        return list.subList(fromIndex, toIndex);
89182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
89282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
893e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private void performLoadItem(String itemId, final ResultReceiver receiver) {
894096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<MediaBrowserCompat.MediaItem> result =
895096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                new Result<MediaBrowserCompat.MediaItem>(itemId) {
896a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                    @Override
897a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                    void onResultSent(MediaBrowserCompat.MediaItem item, @ResultFlags int flag) {
898a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        Bundle bundle = new Bundle();
899a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        bundle.putParcelable(KEY_MEDIA_ITEM, item);
900a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        receiver.send(0, bundle);
901a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                    }
902a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                };
903e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
904096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        MediaBrowserServiceCompat.this.onLoadItem(itemId, result);
905e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
906e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
907e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
908e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for id=" + itemId);
909e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
910e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
911e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
912e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
913e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Contains information that the browser service needs to send to the client
914e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * when first connected.
915e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
916e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final class BrowserRoot {
9178e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
9188e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
9198e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for recently played media items.
9208e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9218e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
9228e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving media items that are recently played.
9238e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
9248e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
9258e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9268e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
9278e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9288e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_OFFLINE
9298e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_SUGGESTED
9308e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
9318e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
9328e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
9338e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
9348e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
9358e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for offline media items.
9368e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9378e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
9388e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving media items that are can be played without an
9398e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * internet connection.
9408e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
9418e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
9428e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9438e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
9448e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9458e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_RECENT
9468e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_SUGGESTED
9478e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
9488e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
9498e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
9508e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
9518e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
9528e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for suggested media items.
9538e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9548e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
9558e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving the media items suggested by the media browser
9569763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo         * service. The list of media items passed in {@link android.support.v4.media.MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded(String, List)}
9579763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo         * is considered ordered by relevance, first being the top suggestion.
9588e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
9598e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
9608e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9618e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
9628e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
9638e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_RECENT
9648e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_OFFLINE
9658e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
9668e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
9678e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
968e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private String mRootId;
969e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private Bundle mExtras;
970e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
971e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
972e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Constructs a browser root.
973e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param rootId The root id for browsing.
974e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param extras Any extras about the browser service.
975e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
976e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
977e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (rootId == null) {
978e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
979e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        "Use null for BrowserRoot instead.");
980e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
981e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mRootId = rootId;
982e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mExtras = extras;
983e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
984e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
985e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
986e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Gets the root id for browsing.
987e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
988e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public String getRootId() {
989e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mRootId;
990e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
991e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
992e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
99323471681e408dfe4e44975e13e7575ab5a04bc8cJae Seo         * Gets any extras about the browser service.
994e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
995e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public Bundle getExtras() {
996e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mExtras;
997e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
998e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
999e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo}
1000