MediaBrowserServiceCompat.java revision fda621da7916073852394d14fcd2cb37f202287d
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<>();
100fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    private ConnectionRecord mCurConnection;
1013f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private final ServiceHandler mHandler = new ServiceHandler();
102e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    MediaSessionCompat.Token mSession;
103e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    interface MediaBrowserServiceImpl {
1053f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        void onCreate();
1069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder onBind(Intent intent);
1077a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        void setSessionToken(MediaSessionCompat.Token token);
108900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        void notifyChildrenChanged(final String parentId, final Bundle options);
109fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        Bundle getBrowserRootHints();
1109703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
1119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl {
1133f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        private Messenger mMessenger;
1149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
1173f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mMessenger = new Messenger(mHandler);
1189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
1229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            if (SERVICE_INTERFACE.equals(intent.getAction())) {
1233f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                return mMessenger.getBinder();
1249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            }
1259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return null;
1269703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1277a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim
1287a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        @Override
1297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void setSessionToken(final MediaSessionCompat.Token token) {
1307a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            mHandler.post(new Runnable() {
1317a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
1327a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                public void run() {
1337a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    for (IBinder key : mConnections.keySet()) {
1347a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        ConnectionRecord connection = mConnections.get(key);
1357a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        try {
1367a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            connection.callbacks.onConnect(connection.root.getRootId(), token,
1377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                    connection.root.getExtras());
1387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        } catch (RemoteException e) {
1397a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
1407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            mConnections.remove(key);
1417a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        }
1427a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    }
1437a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
1447a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            });
1457a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
146900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
147900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
148900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void notifyChildrenChanged(@NonNull final String parentId, final Bundle options) {
149900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            mHandler.post(new Runnable() {
150900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                @Override
151900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                public void run() {
152900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    for (IBinder binder : mConnections.keySet()) {
153900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        ConnectionRecord connection = mConnections.get(binder);
154900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        List<Pair<IBinder, Bundle>> callbackList =
155900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                connection.subscriptions.get(parentId);
156900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        if (callbackList != null) {
157900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            for (Pair<IBinder, Bundle> callback : callbackList) {
158900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                if (MediaBrowserCompatUtils.hasDuplicatedItems(
159900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                        options, callback.second)) {
160900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    performLoadChildren(parentId, connection, callback.second);
161900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                }
162900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            }
163900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        }
164900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    }
165900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                }
166900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            });
167900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
168fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
169fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        @Override
170fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public Bundle getBrowserRootHints() {
171fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            if (mCurConnection == null) {
172fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                throw new IllegalStateException("This should be called inside of onLoadChildren or"
173fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        + " onLoadItem methods");
174fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            }
175fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
176fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        }
1779703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
1789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1797a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl,
1807a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.ServiceCompatProxy {
181900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        Object mServiceObj;
182900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        Messenger mMessenger;
1839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1849703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
1867a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi21.createService(
1877a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    MediaBrowserServiceCompat.this, this);
1887a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
1899703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1909703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1919703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1929703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
1939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent);
1949703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
19582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
19682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
1977a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void setSessionToken(MediaSessionCompat.Token token) {
1987a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.setSessionToken(mServiceObj, token.getToken());
19982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
20082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
20182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
202900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void notifyChildrenChanged(final String parentId, final Bundle options) {
203900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            if (mMessenger == null) {
204900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                // TODO: Support notifyChildrenChanged with options.
205900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
206900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            } else {
207900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                mHandler.post(new Runnable() {
208900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    @Override
209900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    public void run() {
210900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        for (IBinder binder : mConnections.keySet()) {
211900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            ConnectionRecord connection = mConnections.get(binder);
212900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            List<Pair<IBinder, Bundle>> callbackList =
213900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    connection.subscriptions.get(parentId);
214900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            if (callbackList != null) {
215900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                for (Pair<IBinder, Bundle> callback : callbackList) {
216900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    if (MediaBrowserCompatUtils.hasDuplicatedItems(
217900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                            options, callback.second)) {
218900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                        performLoadChildren(parentId, connection, callback.second);
219900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    }
220900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                }
221900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            }
222900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        }
223900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    }
224900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                });
225900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            }
226900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
227900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
228900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
229fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public Bundle getBrowserRootHints() {
230fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            if (mMessenger == null) {
231fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                // TODO: Handle getBrowserRootHints when connected with framework MediaBrowser.
232fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                return null;
233fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            }
234fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            if (mCurConnection == null) {
235fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                throw new IllegalStateException("This should be called inside of onLoadChildren or"
236fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        + " onLoadItem methods");
237fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            }
238fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
239fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        }
240fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
241fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        @Override
2427a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot(
2437a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                String clientPackageName, int clientUid, Bundle rootHints) {
2447a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            Bundle rootExtras = null;
2457a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (rootHints != null && rootHints.getInt(EXTRA_CLIENT_VERSION, 0) != 0) {
2467a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootHints.remove(EXTRA_CLIENT_VERSION);
2477a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                mMessenger = new Messenger(mHandler);
2487a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras = new Bundle();
2497a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
2507a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
2517a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
2527a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
2537a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    clientPackageName, clientUid, rootHints);
2547a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (root == null) {
2557a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                return null;
2567a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
2577a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (rootExtras == null) {
2587a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras = root.getExtras();
2597a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            } else if (root.getExtras() != null) {
2607a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras.putAll(root.getExtras());
2617a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
2627a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return new MediaBrowserServiceCompatApi21.BrowserRoot(
2637a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    root.getRootId(), rootExtras);
26482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
26501b922eddadab50910ce289921001a63855e1f8eSungsoo Lim
26601b922eddadab50910ce289921001a63855e1f8eSungsoo Lim        @Override
2677a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void onLoadChildren(String parentId,
2687a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                final MediaBrowserServiceCompatApi21.ResultWrapper resultWrapper) {
2697a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            final Result<List<MediaBrowserCompat.MediaItem>> result
2707a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
2717a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
272900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flags) {
2737a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    List<Parcel> parcelList = null;
2747a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    if (list != null) {
2757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        parcelList = new ArrayList<>();
2767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        for (MediaBrowserCompat.MediaItem item : list) {
2777a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            Parcel parcel = Parcel.obtain();
2787a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            item.writeToParcel(parcel, 0);
2797a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            parcelList.add(parcel);
2807a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        }
2817a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    }
2827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    resultWrapper.sendResult(parcelList);
2837a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
2847a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim
2857a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
2867a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                public void detach() {
2877a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    resultWrapper.detach();
2887a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
2897a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            };
2907a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompat.this.onLoadChildren(parentId, result);
29101b922eddadab50910ce289921001a63855e1f8eSungsoo Lim        }
29201b922eddadab50910ce289921001a63855e1f8eSungsoo Lim    }
29301b922eddadab50910ce289921001a63855e1f8eSungsoo Lim
294900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim    class MediaBrowserServiceImplApi24 extends MediaBrowserServiceImplApi21 implements
295900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            MediaBrowserServiceCompatApi24.ServiceCompatProxy {
296900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
297900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void onCreate() {
298900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi24.createService(
299900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    MediaBrowserServiceCompat.this, this);
300900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
301900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
302900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
303900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
304900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void notifyChildrenChanged(final String parentId, final Bundle options) {
305900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            if (options == null) {
306900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
307900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            } else {
308900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                MediaBrowserServiceCompatApi24.notifyChildrenChanged(mServiceObj, parentId,
309900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        options);
310900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            }
311900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
312900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
313900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
314900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void onLoadChildren(String parentId,
315900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                final MediaBrowserServiceCompatApi24.ResultWrapper resultWrapper, Bundle options) {
316900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            final Result<List<MediaBrowserCompat.MediaItem>> result
317900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
318900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                @Override
319900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flags) {
320900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    List<Parcel> parcelList = null;
321900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    if (list != null) {
322900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        parcelList = new ArrayList<>();
323900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        for (MediaBrowserCompat.MediaItem item : list) {
324900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            Parcel parcel = Parcel.obtain();
325900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            item.writeToParcel(parcel, 0);
326900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            parcelList.add(parcel);
327900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        }
328900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    }
329900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    resultWrapper.sendResult(parcelList, flags);
330900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                }
331900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
332900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                @Override
333900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                public void detach() {
334900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    resultWrapper.detach();
335900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                }
336900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            };
337900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            MediaBrowserServiceCompat.this.onLoadChildren(parentId, result, options);
338900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
339fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
340fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        @Override
341fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public Bundle getBrowserRootHints() {
342fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            return MediaBrowserServiceCompatApi24.getBrowserRootHints(mServiceObj);
343fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        }
344900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim    }
345900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
3463f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private final class ServiceHandler extends Handler {
3477a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        private final ServiceBinderImpl mServiceBinderImpl = new ServiceBinderImpl();
3483f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
3493f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        @Override
3503f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void handleMessage(Message msg) {
351094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
3523f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            switch (msg.what) {
35382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_CONNECT:
3547a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.connect(data.getString(DATA_PACKAGE_NAME),
355094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                            data.getInt(DATA_CALLING_UID), data.getBundle(DATA_ROOT_HINTS),
3563f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
3573f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
35882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_DISCONNECT:
3597a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo));
3603f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
36182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_ADD_SUBSCRIPTION:
3627a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.addSubscription(data.getString(DATA_MEDIA_ITEM_ID),
3637a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
3647a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            data.getBundle(DATA_OPTIONS),
3657a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
3663f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
36782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_REMOVE_SUBSCRIPTION:
3687a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.removeSubscription(data.getString(DATA_MEDIA_ITEM_ID),
3697a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
3707a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
3713f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
37282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_GET_MEDIA_ITEM:
3737a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.getMediaItem(data.getString(DATA_MEDIA_ITEM_ID),
374fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                            (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER),
375fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
3763f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
37716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                case CLIENT_MSG_REGISTER_CALLBACK_MESSENGER:
378fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    mServiceBinderImpl.registerCallbacks(new ServiceCallbacksCompat(msg.replyTo),
379fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                            data.getBundle(DATA_ROOT_HINTS));
38016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    break;
38199f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                case CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER:
3827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.unregisterCallbacks(new ServiceCallbacksCompat(msg.replyTo));
38399f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    break;
3843f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                default:
38582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    Log.w(TAG, "Unhandled message: " + msg
38682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Service version: " + SERVICE_VERSION_CURRENT
38782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Client version: " + msg.arg1);
3883f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
3893f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
3903f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
391094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        @Override
392094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
393094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // Binder.getCallingUid() in handleMessage will return the uid of this process.
394094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // In order to get the right calling uid, Binder.getCallingUid() should be called here.
395094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
396d4c131eea07dc2593f70523979929e52cf80e31eSungsoo Lim            data.setClassLoader(MediaBrowserCompat.class.getClassLoader());
397094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putInt(DATA_CALLING_UID, Binder.getCallingUid());
398094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            return super.sendMessageAtTime(msg, uptimeMillis);
399094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        }
400094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim
4013f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void postOrRun(Runnable r) {
4023f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            if (Thread.currentThread() == getLooper().getThread()) {
4033f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                r.run();
4043f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            } else {
4053f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                post(r);
4063f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
4073f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
4083f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
4097a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public ServiceBinderImpl getServiceBinderImpl() {
4107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return mServiceBinderImpl;
4113f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
4123f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    }
4133f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
414e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
415e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * All the info about a connection.
416e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
417e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private class ConnectionRecord {
418e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        String pkg;
419e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Bundle rootHints;
4209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        ServiceCallbacks callbacks;
421e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        BrowserRoot root;
4227a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap();
423e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
424e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
425e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
426096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}.
427e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
428e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Each of the methods that takes one of these to send the result must call
42982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@link #sendResult} to respond to the caller with the given results. If those
430e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * functions return without calling {@link #sendResult}, they must instead call
431e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link #detach} before returning, and then may call {@link #sendResult} when
43282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * they are done. If more than one of those methods is called, an exception will
433e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * be thrown.
434e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
435096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadChildren
436096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadItem
437e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
4389703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    public static class Result<T> {
439e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private Object mDebug;
440e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mDetachCalled;
441e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mSendResultCalled;
44282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        private int mFlags;
443e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
444e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Result(Object debug) {
445e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDebug = debug;
446e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
447e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
448e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
449e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Send the result back to the caller.
450e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
451e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void sendResult(T result) {
452e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
453e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
454e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
455e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mSendResultCalled = true;
45682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onResultSent(result, mFlags);
457e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
458e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
459e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
460e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Detach this message from the current thread and allow the {@link #sendResult}
461e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * call to happen later.
462e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
463e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void detach() {
464e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mDetachCalled) {
465e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when detach() had already"
466e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
467e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
468e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
469e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when sendResult() had already"
470e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
471e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
472e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDetachCalled = true;
473e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
474e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
475e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        boolean isDone() {
476e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mDetachCalled || mSendResultCalled;
477e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
478e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
47982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void setFlags(@ResultFlags int flags) {
48082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            mFlags = flags;
48182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
48282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
483e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
484e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Called when the result is sent, after assertions about not being called twice
485e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * have happened.
486e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
48782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void onResultSent(T result, @ResultFlags int flags) {
488e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
489e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
490e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
4917a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private class ServiceBinderImpl {
492094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public void connect(final String pkg, final int uid, final Bundle rootHints,
4939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                final ServiceCallbacks callbacks) {
494e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
495e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (!isValidPackage(pkg, uid)) {
496e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
497e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " package=" + pkg);
498e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
499e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5003f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
5019703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
5029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
5039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
5049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
50582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
5069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    mConnections.remove(b);
5079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
5099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.pkg = pkg;
5109703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.rootHints = rootHints;
5119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.callbacks = callbacks;
5129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.root =
5149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints);
5159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // If they didn't return something, don't allow this client.
5179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection.root == null) {
5189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.i(TAG, "No root for client " + pkg + " from service "
5199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + getClass().getName());
5209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
5219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            callbacks.onConnectFailed();
5229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
5239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
5249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
5259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        }
5269703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    } else {
5279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
5289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.put(b, connection);
5299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            if (mSession != null) {
5309703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                callbacks.onConnect(connection.root.getRootId(),
5319703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                        mSession, connection.root.getExtras());
532e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            }
5339703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
5349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnect() failed. Dropping client. "
5359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
5369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.remove(b);
537e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        }
538e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
5399703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
5409703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
541e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
542e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void disconnect(final ServiceCallbacks callbacks) {
5443f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
5459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
5469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
5479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
5489703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
54982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
5509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord old = mConnections.remove(b);
5519703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (old != null) {
5529703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        // TODO
553e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
5549703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
5559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
556e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
557e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5587a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void addSubscription(final String id, final IBinder token, final Bundle options,
55982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                final ServiceCallbacks callbacks) {
5603f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
5619703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
5629703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
5639703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
5646a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
5659703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // Get the record for the connection
5669703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = mConnections.get(b);
5679703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection == null) {
5689703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.w(TAG, "addSubscription for callback that isn't registered id="
5699703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + id);
5709703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        return;
571493571364635be0190cea8ee230a601070391e6fIan Pedowitz                    }
5729703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5737a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    MediaBrowserServiceCompat.this.addSubscription(id, connection, token, options);
5749703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
5759703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
576e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
577e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5787a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void removeSubscription(final String id, final IBinder token,
57982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                final ServiceCallbacks callbacks) {
5803f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
581e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
582e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
583e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    final IBinder b = callbacks.asBinder();
584e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
585e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(b);
586e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (connection == null) {
587e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
588e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + id);
589e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        return;
590e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
59182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    if (!MediaBrowserServiceCompat.this.removeSubscription(
5927a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            id, connection, token)) {
593e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription called for " + id
594e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " which is not subscribed");
595e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
596e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
597e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
598e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
599e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
600fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public void getMediaItem(final String mediaId, final ResultReceiver receiver,
601fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                final ServiceCallbacks callbacks) {
602e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (TextUtils.isEmpty(mediaId) || receiver == null) {
603e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return;
604e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
605e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
6063f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
607e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
608e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
609fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    final IBinder b = callbacks.asBinder();
610fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
611fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    ConnectionRecord connection = mConnections.get(b);
612fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    if (connection == null) {
613fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
614fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        return;
615fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    }
616fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    performLoadItem(mediaId, connection, receiver);
617e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
618e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
619e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
62016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim
62116d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
622fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public void registerCallbacks(final ServiceCallbacks callbacks, final Bundle rootHints) {
62316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim            mHandler.postOrRun(new Runnable() {
62416d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                @Override
62516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                public void run() {
62616d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    final IBinder b = callbacks.asBinder();
62716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
62816d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    mConnections.remove(b);
62916d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim
63016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
63116d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    connection.callbacks = callbacks;
632fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    connection.rootHints = rootHints;
63316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    mConnections.put(b, connection);
63416d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                }
63516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim            });
63616d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim        }
63799f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim
63899f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
63999f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        public void unregisterCallbacks(final ServiceCallbacks callbacks) {
64099f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim            mHandler.postOrRun(new Runnable() {
64199f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                @Override
64299f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                public void run() {
64399f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    final IBinder b = callbacks.asBinder();
64499f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    mConnections.remove(b);
64599f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                }
64699f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim            });
64799f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        }
648e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
649e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
6509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private interface ServiceCallbacks {
6519703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder asBinder();
6529703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
6539703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
6549703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnectFailed() throws RemoteException;
65582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list, Bundle options)
6569703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
6579703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
6589703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
6599703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private class ServiceCallbacksCompat implements ServiceCallbacks {
6603f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        final Messenger mCallbacks;
6619703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
6623f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        ServiceCallbacksCompat(Messenger callbacks) {
6639703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks = callbacks;
6649703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
6659703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
6669703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder asBinder() {
6673f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            return mCallbacks.getBinder();
6689703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
6699703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
6709703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
6719703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException {
67282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            if (extras == null) {
67382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                extras = new Bundle();
67482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            }
67582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
6763f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Bundle data = new Bundle();
677094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, root);
678094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putParcelable(DATA_MEDIA_SESSION_TOKEN, session);
679094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putBundle(DATA_ROOT_HINTS, extras);
680094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT, data);
6819703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
6829703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
6839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnectFailed() throws RemoteException {
684094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null);
6859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
6869703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
68782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list,
68882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                Bundle options) throws RemoteException {
689094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = new Bundle();
690094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, mediaId);
69182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            data.putBundle(DATA_OPTIONS, options);
692ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            if (list != null) {
693094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                data.putParcelableArrayList(DATA_MEDIA_ITEM_LIST,
694ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim                        list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
695ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            }
696094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, data);
6973f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
6983f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
699094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        private void sendRequest(int what, Bundle data) throws RemoteException {
7003f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Message msg = Message.obtain();
7013f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.what = what;
70282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            msg.arg1 = SERVICE_VERSION_CURRENT;
7033f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.setData(data);
7043f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mCallbacks.send(msg);
7059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
7079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
708e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
709e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void onCreate() {
710e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        super.onCreate();
711900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        if (Build.VERSION.SDK_INT >= 24) {
712900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            mImpl = new MediaBrowserServiceImplApi24();
713900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        } else if (Build.VERSION.SDK_INT >= 21) {
7149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplApi21();
7159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        } else {
7169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplBase();
7179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        mImpl.onCreate();
719e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
720e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
721e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
722e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public IBinder onBind(Intent intent) {
7239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        return mImpl.onBind(intent);
724e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
725e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
726e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
727e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
728e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
729e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
730e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
731e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get the root information for browsing by a particular client.
732e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
733e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The implementation should verify that the client package has permission
734e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * to access browse media information before returning the root id; it
735e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * should return null if the client is not allowed to access this
736e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * information.
737e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * </p>
738e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
739e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientPackageName The package name of the application which is
740e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            requesting access to browse media.
741e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientUid The uid of the application which is requesting access to
742e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            browse media.
743e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param rootHints An optional bundle of service-specific arguments to send
744e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            to the media browse service when connecting and retrieving the
745e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            root id for browsing, or null if none. The contents of this
746e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            bundle may affect the information returned when browsing.
747e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @return The {@link BrowserRoot} for accessing this app's content or null.
74826e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_RECENT
74926e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_OFFLINE
75026e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_SUGGESTED
751e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
752e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
753e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            int clientUid, @Nullable Bundle rootHints);
754e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
755e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
756e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about the children of a media item.
757e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
758e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}
759e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * with the list of children. If loading the children will be an expensive
760e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * operation that should be performed on another thread,
761e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link Result#detach result.detach} may be called before returning from
762e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * this function, and then {@link Result#sendResult result.sendResult}
763e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * called when the loading is complete.
764e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
765e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose children are to be
766e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            queried.
767e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the list of children to, or null if the
768e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            id is invalid.
769e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
770e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract void onLoadChildren(@NonNull String parentId,
771096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result);
772e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
773e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
77482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Called to get information about the children of a media item.
77582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * <p>
77682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Implementations must call {@link Result#sendResult result.sendResult}
77782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * with the list of children. If loading the children will be an expensive
77882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * operation that should be performed on another thread,
77982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@link Result#detach result.detach} may be called before returning from
78082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * this function, and then {@link Result#sendResult result.sendResult}
78182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * called when the loading is complete.
78282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *
78382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param parentId The id of the parent media item whose children are to be
78482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            queried.
78582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param result The Result to send the list of children to, or null if the
78682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            id is invalid.
78782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param options A bundle of service-specific arguments sent from the media
78882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            browse. The information returned through the result should be
78982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            affected by the contents of this bundle.
79082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
79182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void onLoadChildren(@NonNull String parentId,
79282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result, @NonNull Bundle options) {
79382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // To support backward compatibility, when the implementation of MediaBrowserService doesn't
79482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // override onLoadChildren() with options, onLoadChildren() without options will be used
79582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // instead, and the options will be applied in the implementation of result.onResultSent().
79682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED);
79782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        onLoadChildren(parentId, result);
79882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
79982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
80082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
801e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about a specific media item.
802e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
803e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}. If
804e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * loading the item will be an expensive operation {@link Result#detach
805e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * result.detach} may be called before returning from this function, and
806e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * then {@link Result#sendResult result.sendResult} called when the item has
807e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * been loaded.
808e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
809e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The default implementation sends a null result.
810e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
811fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * @param itemId The id for the specific {@link MediaBrowserCompat.MediaItem}.
812e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the item to, or null if the id is
813e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            invalid.
814e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
815096f2531cb790bc1106377d2da344614a3b88d39Jae Seo    public void onLoadItem(String itemId, Result<MediaBrowserCompat.MediaItem> result) {
816e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        result.sendResult(null);
817e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
818e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
819e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
820e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call to set the media session.
821e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
822e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This should be called as soon as possible during the service's startup.
823e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * It may only be called once.
824e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
825096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @param token The token for the service's {@link MediaSessionCompat}.
826e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
8277a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    public void setSessionToken(MediaSessionCompat.Token token) {
828e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (token == null) {
829e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("Session token may not be null.");
830e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
831e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (mSession != null) {
832e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("The session token has already been set.");
833e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
834e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mSession = token;
8357a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        mImpl.setSessionToken(token);
836e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
837e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
838e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
839e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Gets the session token, or null if it has not yet been created
840e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * or if it has been destroyed.
841e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
842e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public @Nullable MediaSessionCompat.Token getSessionToken() {
843e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return mSession;
844e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
845e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
846e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
847fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * Gets the root hints sent from the currently connected {@link MediaBrowserCompat}.
848fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * Note that this will return null when connected to {@link android.media.browse.MediaBrowser}
849fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * and running on API 23 or lower.
850fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     *
851fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren}
852fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     *             or {@link #onLoadItem}
853fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     */
854fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    public final Bundle getBrowserRootHints() {
855fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        return mImpl.getBrowserRootHints();
856fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    }
857fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
858fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    /**
859e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Notifies all connected media browsers that the children of
860e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * the specified parent id have changed in some way.
861e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This will cause browsers to fetch subscribed content again.
862e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
863e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose
864e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * children changed.
865e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
86682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId) {
867900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        if (parentId == null) {
868900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
869900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
870900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        mImpl.notifyChildrenChanged(parentId, null);
87182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
87282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
87382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
87482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Notifies all connected media browsers that the children of
87582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * the specified parent id have changed in some way.
87682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * This will cause browsers to fetch subscribed content again.
87782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *
87882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param parentId The id of the parent media item whose
87982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            children changed.
88082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param options A bundle of service-specific arguments to send
88182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            to the media browse. The contents of this bundle may
88282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            contain the information about the change.
88382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
88482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
885e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (parentId == null) {
886e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
887e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
888900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        if (options == null) {
889900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
890900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
891900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        mImpl.notifyChildrenChanged(parentId, options);
892e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
893e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
894e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
895e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Return whether the given package is one of the ones that is owned by the uid.
896e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
897e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private boolean isValidPackage(String pkg, int uid) {
898e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (pkg == null) {
899e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return false;
900e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
901e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final PackageManager pm = getPackageManager();
902e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final String[] packages = pm.getPackagesForUid(uid);
903e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final int N = packages.length;
904e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        for (int i=0; i<N; i++) {
905e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (packages[i].equals(pkg)) {
906e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return true;
907e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
908e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
909e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return false;
910e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
911e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
912e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
913e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Save the subscription and if it is a new subscription send the results.
914e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
9157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private void addSubscription(String id, ConnectionRecord connection, IBinder token,
9167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            Bundle options) {
917e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // Save the subscription
9187a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
9197a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (callbackList == null) {
9207a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            callbackList = new ArrayList<>();
92182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
9227a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        for (Pair<IBinder, Bundle> callback : callbackList) {
9237a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (token == callback.first
9247a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    && MediaBrowserCompatUtils.areSameOptions(options, callback.second)) {
92582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                return;
92682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
92782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
9287a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        callbackList.add(new Pair<>(token, options));
9297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        connection.subscriptions.put(id, callbackList);
930e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // send the results
93182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        performLoadChildren(id, connection, options);
93282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
93382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
93482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
93582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Remove the subscription.
93682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
9377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
9387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (token == null) {
9397a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return connection.subscriptions.remove(id) != null;
9407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
94182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        boolean removed = false;
9427a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
9437a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (callbackList != null) {
9447a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            for (Pair<IBinder, Bundle> callback : callbackList) {
9457a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                if (token == callback.first) {
94682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    removed = true;
9477a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    callbackList.remove(callback);
94882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                }
94982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
9507a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (callbackList.size() == 0) {
95182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                connection.subscriptions.remove(id);
95282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
95382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
95482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        return removed;
955e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
956e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
957e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
958e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call onLoadChildren and then send the results back to the connection.
959e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
960e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Callers must make sure that this connection is still connected.
961e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
96282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private void performLoadChildren(final String parentId, final ConnectionRecord connection,
96382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            final Bundle options) {
964096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<List<MediaBrowserCompat.MediaItem>> result
965096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
966e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
967900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flags) {
968e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
9697a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    if (DEBUG) {
970e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
971e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
972e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
973e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    return;
974e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
975e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
97682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                List<MediaBrowserCompat.MediaItem> filteredList =
977900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        (flags & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
9787a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                ? applyOptions(list, options) : list;
979e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                try {
98082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    connection.callbacks.onLoadChildren(parentId, filteredList, options);
981e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                } catch (RemoteException ex) {
982e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    // The other side is in the process of crashing.
983e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
984e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            + " package=" + connection.pkg);
985e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
986e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
987e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        };
988e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
989fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = connection;
99082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (options == null) {
99182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onLoadChildren(parentId, result);
99282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        } else {
99382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onLoadChildren(parentId, result, options);
99482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
995fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = null;
996e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
997e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
998e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
999e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for package=" + connection.pkg + " id=" + parentId);
1000e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1001e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
1002e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
100382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private List<MediaBrowserCompat.MediaItem> applyOptions(List<MediaBrowserCompat.MediaItem> list,
100482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            final Bundle options) {
10057a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (list == null) {
10067a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return null;
10077a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
100882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int page = options.getInt(MediaBrowserCompat.EXTRA_PAGE, -1);
100982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int pageSize = options.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1);
101082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (page == -1 && pageSize == -1) {
101182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            return list;
101282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
10137a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        int fromIndex = pageSize * page;
101482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int toIndex = fromIndex + pageSize;
10157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
10167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return Collections.EMPTY_LIST;
101782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
101882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (toIndex > list.size()) {
101982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            toIndex = list.size();
102082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
102182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        return list.subList(fromIndex, toIndex);
102282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
102382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
1024fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    private void performLoadItem(String itemId, ConnectionRecord connection,
1025fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            final ResultReceiver receiver) {
1026096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<MediaBrowserCompat.MediaItem> result =
1027096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                new Result<MediaBrowserCompat.MediaItem>(itemId) {
1028a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                    @Override
1029900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    void onResultSent(MediaBrowserCompat.MediaItem item, @ResultFlags int flags) {
1030a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        Bundle bundle = new Bundle();
1031a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        bundle.putParcelable(KEY_MEDIA_ITEM, item);
1032a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        receiver.send(0, bundle);
1033a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                    }
1034a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                };
1035e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1036fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = connection;
1037fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        onLoadItem(itemId, result);
1038fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = null;
1039e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1040e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
1041e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
1042e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for id=" + itemId);
1043e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1044e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
1045e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1046e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
1047e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Contains information that the browser service needs to send to the client
1048e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * when first connected.
1049e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
1050e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final class BrowserRoot {
10518e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
10528e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
10538e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for recently played media items.
10548e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10558e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
10568e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving media items that are recently played.
10578e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
10588e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
10598e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10608e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
10618e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10628e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_OFFLINE
10638e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_SUGGESTED
10648e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
10658e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
10668e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
10678e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
10688e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
10698e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for offline media items.
10708e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10718e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
10728e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving media items that are can be played without an
10738e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * internet connection.
10748e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
10758e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
10768e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10778e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
10788e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10798e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_RECENT
10808e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_SUGGESTED
10818e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
10828e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
10838e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
10848e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
10858e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
10868e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for suggested media items.
10878e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10888e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
10898e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving the media items suggested by the media browser
10909763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo         * service. The list of media items passed in {@link android.support.v4.media.MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded(String, List)}
10919763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo         * is considered ordered by relevance, first being the top suggestion.
10928e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
10938e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
10948e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10958e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
10968e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
10978e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_RECENT
10988e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_OFFLINE
10998e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
11008e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
11018e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
1102e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private String mRootId;
1103e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private Bundle mExtras;
1104e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1105e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
1106e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Constructs a browser root.
1107e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param rootId The root id for browsing.
1108e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param extras Any extras about the browser service.
1109e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
1110e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
1111e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (rootId == null) {
1112e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
1113e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        "Use null for BrowserRoot instead.");
1114e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
1115e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mRootId = rootId;
1116e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mExtras = extras;
1117e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1118e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1119e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
1120e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Gets the root id for browsing.
1121e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
1122e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public String getRootId() {
1123e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mRootId;
1124e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1125e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1126e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
112723471681e408dfe4e44975e13e7575ab5a04bc8cJae Seo         * Gets any extras about the browser service.
1128e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
1129e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public Bundle getExtras() {
1130e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mExtras;
1131e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1132e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
1133e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo}
1134