MediaBrowserServiceCompat.java revision c39d9c75590eca86a5e7e32a8824ba04a0d42e9b
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
19c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport static android.support.annotation.RestrictTo.Scope.GROUP_ID;
207584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_ADD_SUBSCRIPTION;
217584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_CONNECT;
227584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_DISCONNECT;
237584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_GET_MEDIA_ITEM;
247584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_REGISTER_CALLBACK_MESSENGER;
257584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_REMOVE_SUBSCRIPTION;
267584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER;
277584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_CALLBACK_TOKEN;
287584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_CALLING_UID;
297584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_ID;
307584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_ITEM_LIST;
317584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_MEDIA_SESSION_TOKEN;
327584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_OPTIONS;
337584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_PACKAGE_NAME;
347584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_RESULT_RECEIVER;
357584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.DATA_ROOT_HINTS;
367584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.EXTRA_CLIENT_VERSION;
377584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.EXTRA_MESSENGER_BINDER;
387584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.EXTRA_SERVICE_VERSION;
397584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT;
407584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT_FAILED;
417584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_LOAD_CHILDREN;
427584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seoimport static android.support.v4.media.MediaBrowserProtocol.SERVICE_VERSION_CURRENT;
437584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo
44e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.app.Service;
45e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.Intent;
46e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.pm.PackageManager;
47e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Binder;
489703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Build;
49e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Bundle;
50e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Handler;
51e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.IBinder;
523f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Message;
533f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Messenger;
549703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Parcel;
55e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.RemoteException;
5682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport android.support.annotation.IntDef;
57e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.NonNull;
58e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.Nullable;
59c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
6082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Limimport android.support.v4.app.BundleCompat;
61e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.media.session.MediaSessionCompat;
62ac127f074a064c2b5e87102bd7f6c3e4a5d5cdfdSungsoo Limimport android.support.v4.os.BuildCompat;
63e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.os.ResultReceiver;
64e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.util.ArrayMap;
657a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Limimport android.support.v4.util.Pair;
66e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.text.TextUtils;
67e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.util.Log;
68e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
69e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.FileDescriptor;
70e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.PrintWriter;
7182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.lang.annotation.Retention;
7282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.lang.annotation.RetentionPolicy;
739703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport java.util.ArrayList;
7482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.util.Collections;
7582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.util.HashMap;
767528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Limimport java.util.Iterator;
77e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.util.List;
78e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
79e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/**
80e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Base class for media browse services.
81e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p>
82e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Media browse services enable applications to browse media content provided by an application
8382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * and ask the application to start playing it. They may also be used to control content that
84096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * is already playing by way of a {@link MediaSessionCompat}.
85e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p>
86e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
87e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * To extend this class, you must declare the service in your manifest file with
88e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * an intent filter with the {@link #SERVICE_INTERFACE} action.
89e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
90e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * For example:
91e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p><pre>
92096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * &lt;service android:name=".MyMediaBrowserServiceCompat"
93e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *          android:label="&#64;string/service_name" >
94e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;intent-filter>
959703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim *         &lt;action android:name="android.media.browse.MediaBrowserService" />
96e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;/intent-filter>
97e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * &lt;/service>
98e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </pre>
99e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */
100096f2531cb790bc1106377d2da344614a3b88d39Jae Seopublic abstract class MediaBrowserServiceCompat extends Service {
101b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    static final String TAG = "MBServiceCompat";
102b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
103e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private MediaBrowserServiceImpl mImpl;
1059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
106e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
107e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The {@link Intent} that must be declared as handled by the service.
108e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
1099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
110e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
111e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
112e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * A key for passing the MediaItem to the ResultReceiver in getItem.
113e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
114e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @hide
115e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
116c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
117e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final String KEY_MEDIA_ITEM = "media_item";
118e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
119b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001;
1207ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo    static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 0x00000002;
12182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
12282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /** @hide */
123c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
12482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    @Retention(RetentionPolicy.SOURCE)
1257ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo    @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
1267ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
12782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    private @interface ResultFlags { }
12882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
129b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
130b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    ConnectionRecord mCurConnection;
131b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    final ServiceHandler mHandler = new ServiceHandler();
132e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    MediaSessionCompat.Token mSession;
133e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    interface MediaBrowserServiceImpl {
1353f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        void onCreate();
1369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder onBind(Intent intent);
1377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        void setSessionToken(MediaSessionCompat.Token token);
138900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        void notifyChildrenChanged(final String parentId, final Bundle options);
139fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        Bundle getBrowserRootHints();
1409703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
1419703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl {
1433f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        private Messenger mMessenger;
1449703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
1473f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mMessenger = new Messenger(mHandler);
1489703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1499703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1519703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
1529703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            if (SERVICE_INTERFACE.equals(intent.getAction())) {
1533f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                return mMessenger.getBinder();
1549703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            }
1559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return null;
1569703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1577a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim
1587a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        @Override
1597a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void setSessionToken(final MediaSessionCompat.Token token) {
1607a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            mHandler.post(new Runnable() {
1617a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
1627a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                public void run() {
1637528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim                    Iterator<ConnectionRecord> iter = mConnections.values().iterator();
1647528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim                    while (iter.hasNext()){
1657528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim                        ConnectionRecord connection = iter.next();
1667a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        try {
1677a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            connection.callbacks.onConnect(connection.root.getRootId(), token,
1687a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                    connection.root.getExtras());
1697a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        } catch (RemoteException e) {
1707a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
1717528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim                            iter.remove();
1727a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        }
1737a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    }
1747a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
1757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            });
1767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
177900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
178900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
179900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void notifyChildrenChanged(@NonNull final String parentId, final Bundle options) {
180900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            mHandler.post(new Runnable() {
181900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                @Override
182900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                public void run() {
183900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    for (IBinder binder : mConnections.keySet()) {
184900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        ConnectionRecord connection = mConnections.get(binder);
185900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        List<Pair<IBinder, Bundle>> callbackList =
186900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                connection.subscriptions.get(parentId);
187900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        if (callbackList != null) {
188900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            for (Pair<IBinder, Bundle> callback : callbackList) {
189900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                if (MediaBrowserCompatUtils.hasDuplicatedItems(
190900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                        options, callback.second)) {
191900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    performLoadChildren(parentId, connection, callback.second);
192900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                }
193900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            }
194900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        }
195900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    }
196900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                }
197900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            });
198900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
199fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
200fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        @Override
201fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public Bundle getBrowserRootHints() {
202fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            if (mCurConnection == null) {
203fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                throw new IllegalStateException("This should be called inside of onLoadChildren or"
204fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        + " onLoadItem methods");
205fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            }
206fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
207fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        }
2089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
2099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
2107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl,
2117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.ServiceCompatProxy {
212900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        Object mServiceObj;
213900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        Messenger mMessenger;
2149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
2159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
2169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
2177a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi21.createService(
2187a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    MediaBrowserServiceCompat.this, this);
2197a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
2209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
2219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
2229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
2239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
2249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent);
2259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
22682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
22782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
2287a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void setSessionToken(MediaSessionCompat.Token token) {
2297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompatApi21.setSessionToken(mServiceObj, token.getToken());
23082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
23182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
23282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
233900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void notifyChildrenChanged(final String parentId, final Bundle options) {
234900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            if (mMessenger == null) {
235900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
236900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            } else {
237900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                mHandler.post(new Runnable() {
238900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    @Override
239900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    public void run() {
240900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        for (IBinder binder : mConnections.keySet()) {
241900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            ConnectionRecord connection = mConnections.get(binder);
242900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            List<Pair<IBinder, Bundle>> callbackList =
243900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    connection.subscriptions.get(parentId);
244900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            if (callbackList != null) {
245900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                for (Pair<IBinder, Bundle> callback : callbackList) {
246900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    if (MediaBrowserCompatUtils.hasDuplicatedItems(
247900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                            options, callback.second)) {
248900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                        performLoadChildren(parentId, connection, callback.second);
249900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                    }
250900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                                }
251900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            }
252900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        }
253900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    }
254900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                });
255900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            }
256900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
257900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
258900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
259fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public Bundle getBrowserRootHints() {
260fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            if (mMessenger == null) {
261fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                // TODO: Handle getBrowserRootHints when connected with framework MediaBrowser.
262fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                return null;
263fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            }
264fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            if (mCurConnection == null) {
265fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                throw new IllegalStateException("This should be called inside of onLoadChildren or"
266fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        + " onLoadItem methods");
267fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            }
268fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
269fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        }
270fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
271fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        @Override
2727a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot(
2737a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                String clientPackageName, int clientUid, Bundle rootHints) {
2747a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            Bundle rootExtras = null;
2757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (rootHints != null && rootHints.getInt(EXTRA_CLIENT_VERSION, 0) != 0) {
2767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootHints.remove(EXTRA_CLIENT_VERSION);
2777a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                mMessenger = new Messenger(mHandler);
2787a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras = new Bundle();
2797a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
2807a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
2817a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
2827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
2837a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    clientPackageName, clientUid, rootHints);
2847a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (root == null) {
2857a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                return null;
2867a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
2877a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (rootExtras == null) {
2887a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras = root.getExtras();
2897a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            } else if (root.getExtras() != null) {
2907a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                rootExtras.putAll(root.getExtras());
2917a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            }
2927a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return new MediaBrowserServiceCompatApi21.BrowserRoot(
2937a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    root.getRootId(), rootExtras);
29482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
29501b922eddadab50910ce289921001a63855e1f8eSungsoo Lim
29601b922eddadab50910ce289921001a63855e1f8eSungsoo Lim        @Override
2977a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void onLoadChildren(String parentId,
2982e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                final MediaBrowserServiceCompatApi21.ResultWrapper<List<Parcel>> resultWrapper) {
2997a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            final Result<List<MediaBrowserCompat.MediaItem>> result
3007a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
3017a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
302900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flags) {
3037a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    List<Parcel> parcelList = null;
3047a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    if (list != null) {
3057a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        parcelList = new ArrayList<>();
3067a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        for (MediaBrowserCompat.MediaItem item : list) {
3077a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            Parcel parcel = Parcel.obtain();
3087a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            item.writeToParcel(parcel, 0);
3097a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            parcelList.add(parcel);
3107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                        }
3117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    }
3127a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    resultWrapper.sendResult(parcelList);
3137a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
3147a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim
3157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                @Override
3167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                public void detach() {
3177a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    resultWrapper.detach();
3187a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                }
3197a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            };
3207a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            MediaBrowserServiceCompat.this.onLoadChildren(parentId, result);
32101b922eddadab50910ce289921001a63855e1f8eSungsoo Lim        }
32201b922eddadab50910ce289921001a63855e1f8eSungsoo Lim    }
32301b922eddadab50910ce289921001a63855e1f8eSungsoo Lim
3242e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim    class MediaBrowserServiceImplApi23 extends MediaBrowserServiceImplApi21 implements
3252e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim            MediaBrowserServiceCompatApi23.ServiceCompatProxy {
3262e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim        @Override
3272e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim        public void onCreate() {
3282e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi23.createService(
3292e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                    MediaBrowserServiceCompat.this, this);
3302e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
3312e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim        }
3322e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim
3332e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim        @Override
3342e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim        public void onLoadItem(String itemId,
3352e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                final MediaBrowserServiceCompatApi21.ResultWrapper<Parcel> resultWrapper) {
3362e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim            final Result<MediaBrowserCompat.MediaItem> result
3372e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                    = new Result<MediaBrowserCompat.MediaItem>(itemId) {
3382e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                @Override
3392e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                void onResultSent(MediaBrowserCompat.MediaItem item, @ResultFlags int flags) {
340777d23227fb217431dc1c50b25be6ffd60999bdeSungsoo                    if (item == null) {
341777d23227fb217431dc1c50b25be6ffd60999bdeSungsoo                        resultWrapper.sendResult(null);
342777d23227fb217431dc1c50b25be6ffd60999bdeSungsoo                    } else {
343777d23227fb217431dc1c50b25be6ffd60999bdeSungsoo                        Parcel parcelItem = Parcel.obtain();
344777d23227fb217431dc1c50b25be6ffd60999bdeSungsoo                        item.writeToParcel(parcelItem, 0);
345777d23227fb217431dc1c50b25be6ffd60999bdeSungsoo                        resultWrapper.sendResult(parcelItem);
346777d23227fb217431dc1c50b25be6ffd60999bdeSungsoo                    }
3472e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                }
3482e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim
3492e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                @Override
3502e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                public void detach() {
3512e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                    resultWrapper.detach();
3522e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim                }
3532e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim            };
3542e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim            MediaBrowserServiceCompat.this.onLoadItem(itemId, result);
3552e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim        }
3562e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim    }
3572e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim
3582e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim    class MediaBrowserServiceImplApi24 extends MediaBrowserServiceImplApi23 implements
359900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            MediaBrowserServiceCompatApi24.ServiceCompatProxy {
360900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
361900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void onCreate() {
362900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi24.createService(
363900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    MediaBrowserServiceCompat.this, this);
364900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
365900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
366900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
367900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
368900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void notifyChildrenChanged(final String parentId, final Bundle options) {
369900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            if (options == null) {
370900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
371900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            } else {
372900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                MediaBrowserServiceCompatApi24.notifyChildrenChanged(mServiceObj, parentId,
373900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        options);
374900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            }
375900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
376900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
377900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        @Override
378900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        public void onLoadChildren(String parentId,
379900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                final MediaBrowserServiceCompatApi24.ResultWrapper resultWrapper, Bundle options) {
380900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            final Result<List<MediaBrowserCompat.MediaItem>> result
381900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
382900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                @Override
383900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flags) {
384900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    List<Parcel> parcelList = null;
385900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    if (list != null) {
386900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        parcelList = new ArrayList<>();
387900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        for (MediaBrowserCompat.MediaItem item : list) {
388900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            Parcel parcel = Parcel.obtain();
389900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            item.writeToParcel(parcel, 0);
390900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                            parcelList.add(parcel);
391900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        }
392900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    }
393900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    resultWrapper.sendResult(parcelList, flags);
394900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                }
395900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
396900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                @Override
397900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                public void detach() {
398900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    resultWrapper.detach();
399900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                }
400900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            };
401900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            MediaBrowserServiceCompat.this.onLoadChildren(parentId, result, options);
402900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
403fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
404fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        @Override
405fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public Bundle getBrowserRootHints() {
406fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            return MediaBrowserServiceCompatApi24.getBrowserRootHints(mServiceObj);
407fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        }
408900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim    }
409900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim
4103f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private final class ServiceHandler extends Handler {
4117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        private final ServiceBinderImpl mServiceBinderImpl = new ServiceBinderImpl();
4123f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
413b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas        ServiceHandler() {
414b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas        }
415b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas
4163f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        @Override
4173f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void handleMessage(Message msg) {
418094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
4193f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            switch (msg.what) {
42082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_CONNECT:
4217a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.connect(data.getString(DATA_PACKAGE_NAME),
422094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                            data.getInt(DATA_CALLING_UID), data.getBundle(DATA_ROOT_HINTS),
4233f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
4243f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
42582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_DISCONNECT:
4267a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo));
4273f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
42882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_ADD_SUBSCRIPTION:
4297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.addSubscription(data.getString(DATA_MEDIA_ITEM_ID),
4307a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
4317a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            data.getBundle(DATA_OPTIONS),
4327a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
4333f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
43482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_REMOVE_SUBSCRIPTION:
4357a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.removeSubscription(data.getString(DATA_MEDIA_ITEM_ID),
4367a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN),
4377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
4383f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
43982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_GET_MEDIA_ITEM:
4407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.getMediaItem(data.getString(DATA_MEDIA_ITEM_ID),
441fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                            (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER),
442fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
4433f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
44416d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                case CLIENT_MSG_REGISTER_CALLBACK_MESSENGER:
445fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    mServiceBinderImpl.registerCallbacks(new ServiceCallbacksCompat(msg.replyTo),
446fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                            data.getBundle(DATA_ROOT_HINTS));
44716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    break;
44899f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                case CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER:
4497a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    mServiceBinderImpl.unregisterCallbacks(new ServiceCallbacksCompat(msg.replyTo));
45099f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    break;
4513f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                default:
45282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    Log.w(TAG, "Unhandled message: " + msg
45382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Service version: " + SERVICE_VERSION_CURRENT
45482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Client version: " + msg.arg1);
4553f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
4563f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
4573f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
458094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        @Override
459094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
460094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // Binder.getCallingUid() in handleMessage will return the uid of this process.
461094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // In order to get the right calling uid, Binder.getCallingUid() should be called here.
462094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
463d4c131eea07dc2593f70523979929e52cf80e31eSungsoo Lim            data.setClassLoader(MediaBrowserCompat.class.getClassLoader());
464094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putInt(DATA_CALLING_UID, Binder.getCallingUid());
465094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            return super.sendMessageAtTime(msg, uptimeMillis);
466094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        }
467094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim
4683f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void postOrRun(Runnable r) {
4693f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            if (Thread.currentThread() == getLooper().getThread()) {
4703f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                r.run();
4713f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            } else {
4723f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                post(r);
4733f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
4743f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
4753f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    }
4763f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
477e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
478e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * All the info about a connection.
479e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
480e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private class ConnectionRecord {
481e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        String pkg;
482e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Bundle rootHints;
4839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        ServiceCallbacks callbacks;
484e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        BrowserRoot root;
4857a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap();
486b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas
487b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas        ConnectionRecord() {
488b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas        }
489e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
490e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
491e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
492096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}.
493e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
494e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Each of the methods that takes one of these to send the result must call
49582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@link #sendResult} to respond to the caller with the given results. If those
496e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * functions return without calling {@link #sendResult}, they must instead call
497e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link #detach} before returning, and then may call {@link #sendResult} when
49882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * they are done. If more than one of those methods is called, an exception will
499e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * be thrown.
500e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
501096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadChildren
502096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadItem
503e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
5049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    public static class Result<T> {
505e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private Object mDebug;
506e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mDetachCalled;
507e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mSendResultCalled;
50882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        private int mFlags;
509e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
510e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Result(Object debug) {
511e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDebug = debug;
512e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
513e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
514e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
515e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Send the result back to the caller.
516e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
517e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void sendResult(T result) {
518e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
519e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
520e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
521e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mSendResultCalled = true;
52282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onResultSent(result, mFlags);
523e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
524e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
525e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
526e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Detach this message from the current thread and allow the {@link #sendResult}
527e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * call to happen later.
528e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
529e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void detach() {
530e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mDetachCalled) {
531e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when detach() had already"
532e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
533e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
534e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
535e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when sendResult() had already"
536e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
537e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
538e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDetachCalled = true;
539e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
540e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
541e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        boolean isDone() {
542e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mDetachCalled || mSendResultCalled;
543e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
544e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
54582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void setFlags(@ResultFlags int flags) {
54682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            mFlags = flags;
54782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
54882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
549e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
550e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Called when the result is sent, after assertions about not being called twice
551e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * have happened.
552e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
55382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void onResultSent(T result, @ResultFlags int flags) {
554e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
555e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
556e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5577a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    private class ServiceBinderImpl {
558b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas        ServiceBinderImpl() {
559b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas        }
560b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas
561094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public void connect(final String pkg, final int uid, final Bundle rootHints,
5629703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                final ServiceCallbacks callbacks) {
563e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
564e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (!isValidPackage(pkg, uid)) {
565e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
566e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " package=" + pkg);
567e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
568e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
5693f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
5709703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
5719703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
5729703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
5739703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
57482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
5759703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    mConnections.remove(b);
5769703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5779703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
5789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.pkg = pkg;
5799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.rootHints = rootHints;
5809703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.callbacks = callbacks;
5819703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5829703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.root =
5839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints);
5849703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // If they didn't return something, don't allow this client.
5869703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection.root == null) {
5879703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.i(TAG, "No root for client " + pkg + " from service "
5889703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + getClass().getName());
5899703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
5909703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            callbacks.onConnectFailed();
5919703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
5929703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
5939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
5949703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        }
5959703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    } else {
5969703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
5979703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.put(b, connection);
5989703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            if (mSession != null) {
5999703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                callbacks.onConnect(connection.root.getRootId(),
6009703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                        mSession, connection.root.getExtras());
601e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            }
6029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
6039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnect() failed. Dropping client. "
6049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
6059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.remove(b);
606e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        }
607e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
6089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
6099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
610e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
611e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
6129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void disconnect(final ServiceCallbacks callbacks) {
6133f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
6149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
6159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
6169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
6179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
61882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
6199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord old = mConnections.remove(b);
6209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (old != null) {
6219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        // TODO
622e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
6239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
6249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
625e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
626e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
6277a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void addSubscription(final String id, final IBinder token, final Bundle options,
62882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                final ServiceCallbacks callbacks) {
6293f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
6309703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
6319703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
6329703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
6336a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
6349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // Get the record for the connection
6359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = mConnections.get(b);
6369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection == null) {
6379703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.w(TAG, "addSubscription for callback that isn't registered id="
6389703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + id);
6399703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        return;
640493571364635be0190cea8ee230a601070391e6fIan Pedowitz                    }
6419703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
6427a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    MediaBrowserServiceCompat.this.addSubscription(id, connection, token, options);
6439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
6449703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
645e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
646e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
6477a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        public void removeSubscription(final String id, final IBinder token,
64882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                final ServiceCallbacks callbacks) {
6493f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
650e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
651e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
652e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    final IBinder b = callbacks.asBinder();
653e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
654e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(b);
655e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (connection == null) {
656e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
657e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + id);
658e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        return;
659e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
66082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    if (!MediaBrowserServiceCompat.this.removeSubscription(
6617a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                            id, connection, token)) {
662e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription called for " + id
663e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " which is not subscribed");
664e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
665e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
666e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
667e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
668e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
669fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public void getMediaItem(final String mediaId, final ResultReceiver receiver,
670fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                final ServiceCallbacks callbacks) {
671e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (TextUtils.isEmpty(mediaId) || receiver == null) {
672e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return;
673e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
674e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
6753f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
676e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
677e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
678fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    final IBinder b = callbacks.asBinder();
679fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
680fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    ConnectionRecord connection = mConnections.get(b);
681fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    if (connection == null) {
682fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
683fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                        return;
684fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    }
685fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    performLoadItem(mediaId, connection, receiver);
686e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
687e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
688e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
68916d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim
69016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
691fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        public void registerCallbacks(final ServiceCallbacks callbacks, final Bundle rootHints) {
69216d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim            mHandler.postOrRun(new Runnable() {
69316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                @Override
69416d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                public void run() {
69516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    final IBinder b = callbacks.asBinder();
69616d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    // Clear out the old subscriptions. We are getting new ones.
69716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    mConnections.remove(b);
69816d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim
69916d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
70016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    connection.callbacks = callbacks;
701fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim                    connection.rootHints = rootHints;
70216d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                    mConnections.put(b, connection);
70316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim                }
70416d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim            });
70516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim        }
70699f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim
70799f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used.
70899f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        public void unregisterCallbacks(final ServiceCallbacks callbacks) {
70999f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim            mHandler.postOrRun(new Runnable() {
71099f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                @Override
71199f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                public void run() {
71299f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    final IBinder b = callbacks.asBinder();
71399f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                    mConnections.remove(b);
71499f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim                }
71599f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim            });
71699f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim        }
717e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
718e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
7199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private interface ServiceCallbacks {
7209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder asBinder();
7219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
7229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
7239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnectFailed() throws RemoteException;
72482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list, Bundle options)
7259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
7269703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
7279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
7289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private class ServiceCallbacksCompat implements ServiceCallbacks {
7293f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        final Messenger mCallbacks;
7309703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
7313f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        ServiceCallbacksCompat(Messenger callbacks) {
7329703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks = callbacks;
7339703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
73515375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
7369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder asBinder() {
7373f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            return mCallbacks.getBinder();
7389703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7399703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
74015375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
7419703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
7429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException {
74382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            if (extras == null) {
74482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                extras = new Bundle();
74582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            }
74682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
7473f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Bundle data = new Bundle();
748094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, root);
749094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putParcelable(DATA_MEDIA_SESSION_TOKEN, session);
750094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putBundle(DATA_ROOT_HINTS, extras);
751094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT, data);
7529703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7539703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
75415375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
7559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnectFailed() throws RemoteException {
756094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null);
7579703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7589703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
75915375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas        @Override
76082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list,
76182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                Bundle options) throws RemoteException {
762094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = new Bundle();
763094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, mediaId);
76482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            data.putBundle(DATA_OPTIONS, options);
765ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            if (list != null) {
766094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                data.putParcelableArrayList(DATA_MEDIA_ITEM_LIST,
767ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim                        list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
768ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            }
769094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, data);
7703f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
7713f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
772094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        private void sendRequest(int what, Bundle data) throws RemoteException {
7733f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Message msg = Message.obtain();
7743f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.what = what;
77582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            msg.arg1 = SERVICE_VERSION_CURRENT;
7763f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.setData(data);
7773f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mCallbacks.send(msg);
7789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
7809703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
781e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
782e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void onCreate() {
783e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        super.onCreate();
784ac127f074a064c2b5e87102bd7f6c3e4a5d5cdfdSungsoo Lim        if (Build.VERSION.SDK_INT >= 24 || BuildCompat.isAtLeastN()) {
785900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            mImpl = new MediaBrowserServiceImplApi24();
7862e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim        } else if (Build.VERSION.SDK_INT >= 23) {
7872e7d3a28e00195c160cc4f5668e2d7f64eb590daSungsoo Lim            mImpl = new MediaBrowserServiceImplApi23();
788900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        } else if (Build.VERSION.SDK_INT >= 21) {
7899703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplApi21();
7909703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        } else {
7919703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplBase();
7929703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
7939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        mImpl.onCreate();
794e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
795e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
796e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
797e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public IBinder onBind(Intent intent) {
7989703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        return mImpl.onBind(intent);
799e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
800e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
801e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
802e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
803e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
804e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
805e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
806e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get the root information for browsing by a particular client.
807e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
808e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The implementation should verify that the client package has permission
809e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * to access browse media information before returning the root id; it
810e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * should return null if the client is not allowed to access this
811e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * information.
812e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * </p>
813e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
814e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientPackageName The package name of the application which is
815e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            requesting access to browse media.
816e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientUid The uid of the application which is requesting access to
817e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            browse media.
818e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param rootHints An optional bundle of service-specific arguments to send
819e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            to the media browse service when connecting and retrieving the
820e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            root id for browsing, or null if none. The contents of this
821e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            bundle may affect the information returned when browsing.
822e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @return The {@link BrowserRoot} for accessing this app's content or null.
82326e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_RECENT
82426e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_OFFLINE
82526e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo     * @see BrowserRoot#EXTRA_SUGGESTED
826ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim     * @see BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
827e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
828e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
829e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            int clientUid, @Nullable Bundle rootHints);
830e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
831e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
832e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about the children of a media item.
833e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
834e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}
835e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * with the list of children. If loading the children will be an expensive
836e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * operation that should be performed on another thread,
837e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link Result#detach result.detach} may be called before returning from
838e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * this function, and then {@link Result#sendResult result.sendResult}
839e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * called when the loading is complete.
840e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
841e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose children are to be
842e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            queried.
843e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the list of children to, or null if the
844e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            id is invalid.
845e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
846e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract void onLoadChildren(@NonNull String parentId,
847096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result);
848e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
849e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
85082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Called to get information about the children of a media item.
85182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * <p>
85282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Implementations must call {@link Result#sendResult result.sendResult}
85382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * with the list of children. If loading the children will be an expensive
85482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * operation that should be performed on another thread,
85582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * {@link Result#detach result.detach} may be called before returning from
85682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * this function, and then {@link Result#sendResult result.sendResult}
85782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * called when the loading is complete.
85882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *
85982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param parentId The id of the parent media item whose children are to be
86082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            queried.
86182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param result The Result to send the list of children to, or null if the
86282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            id is invalid.
86382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param options A bundle of service-specific arguments sent from the media
86482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            browse. The information returned through the result should be
86582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            affected by the contents of this bundle.
86682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
86782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void onLoadChildren(@NonNull String parentId,
86882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result, @NonNull Bundle options) {
86982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // To support backward compatibility, when the implementation of MediaBrowserService doesn't
87082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // override onLoadChildren() with options, onLoadChildren() without options will be used
87182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        // instead, and the options will be applied in the implementation of result.onResultSent().
87282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED);
87382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        onLoadChildren(parentId, result);
87482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
87582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
87682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
877e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about a specific media item.
878e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
879e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}. If
880e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * loading the item will be an expensive operation {@link Result#detach
881e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * result.detach} may be called before returning from this function, and
882e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * then {@link Result#sendResult result.sendResult} called when the item has
883e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * been loaded.
8847ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo     * </p><p>
8857ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo     * When the given {@code itemId} is invalid, implementations must call
8867ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo     * {@link Result#sendResult result.sendResult} with {@code null}.
8877ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo     * </p><p>
8887ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo     * The default implementation will invoke {@link MediaBrowserCompat.ItemCallback#onError}.
889e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
890fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * @param itemId The id for the specific {@link MediaBrowserCompat.MediaItem}.
891e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the item to, or null if the id is
892e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            invalid.
893e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
894096f2531cb790bc1106377d2da344614a3b88d39Jae Seo    public void onLoadItem(String itemId, Result<MediaBrowserCompat.MediaItem> result) {
8957ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo        result.setFlags(RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED);
896e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        result.sendResult(null);
897e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
898e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
899e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
900e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call to set the media session.
901e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
902e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This should be called as soon as possible during the service's startup.
903e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * It may only be called once.
904e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
905096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @param token The token for the service's {@link MediaSessionCompat}.
906e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
9077a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim    public void setSessionToken(MediaSessionCompat.Token token) {
908e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (token == null) {
909e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("Session token may not be null.");
910e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
911e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (mSession != null) {
912e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("The session token has already been set.");
913e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
914e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mSession = token;
9157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        mImpl.setSessionToken(token);
916e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
917e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
918e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
919e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Gets the session token, or null if it has not yet been created
920e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * or if it has been destroyed.
921e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
922e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public @Nullable MediaSessionCompat.Token getSessionToken() {
923e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return mSession;
924e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
925e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
926e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
927fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * Gets the root hints sent from the currently connected {@link MediaBrowserCompat}.
9287584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo     * The root hints are service-specific arguments included in an optional bundle sent to the
9297584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo     * media browser service when connecting and retrieving the root id for browsing, or null if
9307584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo     * none. The contents of this bundle may affect the information returned when browsing.
9317584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo     * <p>
932fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * Note that this will return null when connected to {@link android.media.browse.MediaBrowser}
933fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * and running on API 23 or lower.
934fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     *
935fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren}
936fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     *             or {@link #onLoadItem}
9377584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo     * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_RECENT
9387584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo     * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_OFFLINE
9397584aa62c9a0b01631c70512899a51ea7ccdc27eJae Seo     * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_SUGGESTED
940ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim     * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
941fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim     */
942fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    public final Bundle getBrowserRootHints() {
943fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        return mImpl.getBrowserRootHints();
944fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    }
945fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim
946fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim    /**
947e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Notifies all connected media browsers that the children of
948e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * the specified parent id have changed in some way.
949e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This will cause browsers to fetch subscribed content again.
950e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
951e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose
952e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * children changed.
953e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
95482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId) {
955900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        if (parentId == null) {
956900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
957900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
958900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        mImpl.notifyChildrenChanged(parentId, null);
95982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
96082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
96182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
96282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Notifies all connected media browsers that the children of
96382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * the specified parent id have changed in some way.
96482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * This will cause browsers to fetch subscribed content again.
96582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *
96682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param parentId The id of the parent media item whose
96782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            children changed.
96882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * @param options A bundle of service-specific arguments to send
96982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            to the media browse. The contents of this bundle may
97082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     *            contain the information about the change.
97182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
97282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
973e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (parentId == null) {
974e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
975e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
976900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        if (options == null) {
977900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
978900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        }
979900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim        mImpl.notifyChildrenChanged(parentId, options);
980e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
981e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
982e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
983e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Return whether the given package is one of the ones that is owned by the uid.
984e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
985b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    boolean isValidPackage(String pkg, int uid) {
986e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (pkg == null) {
987e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return false;
988e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
989e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final PackageManager pm = getPackageManager();
990e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final String[] packages = pm.getPackagesForUid(uid);
991e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final int N = packages.length;
992e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        for (int i=0; i<N; i++) {
993e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (packages[i].equals(pkg)) {
994e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return true;
995e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
996e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
997e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return false;
998e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
999e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1000e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
1001e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Save the subscription and if it is a new subscription send the results.
1002e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
1003b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    void addSubscription(String id, ConnectionRecord connection, IBinder token,
10047a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            Bundle options) {
1005e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // Save the subscription
10067a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
10077a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (callbackList == null) {
10087a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            callbackList = new ArrayList<>();
100982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
10107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        for (Pair<IBinder, Bundle> callback : callbackList) {
10117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (token == callback.first
10127a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    && MediaBrowserCompatUtils.areSameOptions(options, callback.second)) {
101382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                return;
101482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
101582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
10167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        callbackList.add(new Pair<>(token, options));
10177a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        connection.subscriptions.put(id, callbackList);
1018e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // send the results
101982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        performLoadChildren(id, connection, options);
102082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
102182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
102282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    /**
102382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     * Remove the subscription.
102482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim     */
1025b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
10267a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (token == null) {
10277a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return connection.subscriptions.remove(id) != null;
10287a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
102982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        boolean removed = false;
10307a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
10317a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (callbackList != null) {
10327528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim            Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
10337528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim            while (iter.hasNext()){
10347528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim                if (token == iter.next().first) {
103582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    removed = true;
10367528c2286abed5012e9759e3b122cfcf5e5b54bfSungsoo Lim                    iter.remove();
103782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                }
103882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
10397a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            if (callbackList.size() == 0) {
104082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                connection.subscriptions.remove(id);
104182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            }
104282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
104382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        return removed;
1044e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
1045e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1046e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
1047e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call onLoadChildren and then send the results back to the connection.
1048e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
1049e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Callers must make sure that this connection is still connected.
1050e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
1051b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    void performLoadChildren(final String parentId, final ConnectionRecord connection,
105282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            final Bundle options) {
1053096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<List<MediaBrowserCompat.MediaItem>> result
1054096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
1055e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
1056900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim            void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flags) {
1057e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
10587a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                    if (DEBUG) {
1059e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
1060e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
1061e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
1062e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    return;
1063e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
1064e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
106582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                List<MediaBrowserCompat.MediaItem> filteredList =
1066900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                        (flags & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
10677a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim                                ? applyOptions(list, options) : list;
1068e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                try {
106982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim                    connection.callbacks.onLoadChildren(parentId, filteredList, options);
1070e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                } catch (RemoteException ex) {
1071e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    // The other side is in the process of crashing.
1072e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
1073e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            + " package=" + connection.pkg);
1074e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
1075e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
1076e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        };
1077e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1078fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = connection;
107982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (options == null) {
108082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onLoadChildren(parentId, result);
108182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        } else {
108282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            onLoadChildren(parentId, result, options);
108382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
1084fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = null;
1085e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1086e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
1087e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
1088e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for package=" + connection.pkg + " id=" + parentId);
1089e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1090e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
1091e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1092b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    List<MediaBrowserCompat.MediaItem> applyOptions(List<MediaBrowserCompat.MediaItem> list,
109382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            final Bundle options) {
10947a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (list == null) {
10957a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return null;
10967a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        }
109782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int page = options.getInt(MediaBrowserCompat.EXTRA_PAGE, -1);
109882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int pageSize = options.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1);
109982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (page == -1 && pageSize == -1) {
110082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            return list;
110182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
11027a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        int fromIndex = pageSize * page;
110382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        int toIndex = fromIndex + pageSize;
11047a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim        if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
11057a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim            return Collections.EMPTY_LIST;
110682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
110782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        if (toIndex > list.size()) {
110882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim            toIndex = list.size();
110982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        }
111082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim        return list.subList(fromIndex, toIndex);
111182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim    }
111282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim
1113b768ed3824de2c109411654b3830feabe564ff0aAurimas Liutikas    void performLoadItem(String itemId, ConnectionRecord connection,
1114fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim            final ResultReceiver receiver) {
1115096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<MediaBrowserCompat.MediaItem> result =
1116096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                new Result<MediaBrowserCompat.MediaItem>(itemId) {
1117a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                    @Override
1118900e852d4f8b423ec2d8374f8e6743129e8f331bSungsoo Lim                    void onResultSent(MediaBrowserCompat.MediaItem item, @ResultFlags int flags) {
11197ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo                        if ((flags & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
11207ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo                            receiver.send(-1, null);
11217ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo                            return;
11227ce86ec087d5246c3b9a6f038bb538957606c311Sungsoo                        }
1123a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        Bundle bundle = new Bundle();
1124a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        bundle.putParcelable(KEY_MEDIA_ITEM, item);
1125a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                        receiver.send(0, bundle);
1126a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                    }
1127a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim                };
1128e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1129fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = connection;
1130fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        onLoadItem(itemId, result);
1131fda621da7916073852394d14fcd2cb37f202287dSungsoo Lim        mCurConnection = null;
1132e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1133e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
1134e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
1135e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for id=" + itemId);
1136e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1137e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
1138e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1139e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
1140e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Contains information that the browser service needs to send to the client
1141e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * when first connected.
1142e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
1143e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final class BrowserRoot {
11448e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
11458e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
11468e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for recently played media items.
11478e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11488e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
11498e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving media items that are recently played.
11508e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
11518e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
11528e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11538e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
11548e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11558e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_OFFLINE
11568e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_SUGGESTED
1157ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * @see #EXTRA_SUGGESTION_KEYWORDS
11588e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
11598e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
11608e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
11618e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
11628e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
11638e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for offline media items.
11648e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11658e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
11668e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving media items that are can be played without an
11678e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * internet connection.
11688e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
11698e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
11708e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11718e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
11728e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11738e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_RECENT
11748e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_SUGGESTED
1175ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * @see #EXTRA_SUGGESTION_KEYWORDS
11768e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
11778e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
11788e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
11798e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        /**
11808e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
11818e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * browser root for suggested media items.
11828e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11838e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
11848e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * supplied as a root hint for retrieving the media items suggested by the media browser
11859763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo         * service. The list of media items passed in {@link android.support.v4.media.MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded(String, List)}
11869763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo         * is considered ordered by relevance, first being the top suggestion.
11878e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * If the media browser service can provide such media items, the implementation must return
11888e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
11898e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11908e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * <p>The root hint may contain multiple keys.
11918e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         *
11928e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_RECENT
11938e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         * @see #EXTRA_OFFLINE
1194ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * @see #EXTRA_SUGGESTION_KEYWORDS
11958e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo         */
11968e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
11978e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo
1198ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim        /**
1199ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * The lookup key for a string that indicates specific keywords which will be considered
1200ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * when the browser service suggests media items.
1201ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         *
1202ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * <p>When creating a media browser for a given media browser service, this key can be
1203ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * supplied as a root hint together with {@link #EXTRA_SUGGESTED} for retrieving suggested
1204ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * media items related with the keywords. The list of media items passed in
1205ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
1206ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * is considered ordered by relevance, first being the top suggestion.
1207ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * If the media browser service can provide such media items, the implementation must return
1208ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
1209ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         *
1210ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * <p>The root hint may contain multiple keys.
1211ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         *
1212ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * @see #EXTRA_RECENT
1213ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * @see #EXTRA_OFFLINE
1214ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         * @see #EXTRA_SUGGESTED
1215ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim         */
1216ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim        public static final String EXTRA_SUGGESTION_KEYWORDS
1217ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim                = "android.service.media.extra.SUGGESTION_KEYWORDS";
1218ae736327deea12ceb1929f2060acb76b31359c54Sungsoo Lim
1219e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private String mRootId;
1220e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private Bundle mExtras;
1221e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1222e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
1223e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Constructs a browser root.
1224e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param rootId The root id for browsing.
1225e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param extras Any extras about the browser service.
1226e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
1227e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
1228e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (rootId == null) {
1229e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
1230e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        "Use null for BrowserRoot instead.");
1231e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
1232e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mRootId = rootId;
1233e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mExtras = extras;
1234e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1235e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1236e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
1237e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Gets the root id for browsing.
1238e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
1239e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public String getRootId() {
1240e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mRootId;
1241e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1242e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1243e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
124423471681e408dfe4e44975e13e7575ab5a04bc8cJae Seo         * Gets any extras about the browser service.
1245e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
1246e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public Bundle getExtras() {
1247e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mExtras;
1248e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
1249e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
1250e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo}
1251