MediaBrowserService.java revision 88a03d6e62edaac715081e091da7e9abc21d2c0d
117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen/*
217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Copyright (C) 2014 The Android Open Source Project
317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Licensed under the Apache License, Version 2.0 (the "License");
517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * you may not use this file except in compliance with the License.
617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * You may obtain a copy of the License at
717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *      http://www.apache.org/licenses/LICENSE-2.0
917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
1017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Unless required by applicable law or agreed to in writing, software
1117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * distributed under the License is distributed on an "AS IS" BASIS,
1217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * See the License for the specific language governing permissions and
1417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * limitations under the License.
1517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */
1617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
173625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikpackage android.service.media;
1817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
198e3fb586219ecfa82cff841b121543752a79f1faSungsoo Limimport android.annotation.IntDef;
2017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.annotation.NonNull;
2117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.annotation.Nullable;
2217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.annotation.SdkConstant;
2317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.annotation.SdkConstant.SdkConstantType;
2417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.app.Service;
2517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.Intent;
2617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.pm.PackageManager;
2717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.content.pm.ParceledListSlice;
283625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.media.browse.MediaBrowser;
2941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Limimport android.media.browse.MediaBrowserUtils;
3017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.media.session.MediaSession;
3117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Binder;
3217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Bundle;
3317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Handler;
3441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Limimport android.os.IBinder;
3517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.RemoteException;
36b1c88103fea75a19100d534e28366da0144d8138RoboErikimport android.os.ResultReceiver;
373625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.service.media.IMediaBrowserService;
383625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.service.media.IMediaBrowserServiceCallbacks;
39b1c88103fea75a19100d534e28366da0144d8138RoboErikimport android.text.TextUtils;
4017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.util.ArrayMap;
4117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.util.Log;
4217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
4317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.io.FileDescriptor;
4417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.io.PrintWriter;
458e3fb586219ecfa82cff841b121543752a79f1faSungsoo Limimport java.lang.annotation.Retention;
468e3fb586219ecfa82cff841b121543752a79f1faSungsoo Limimport java.lang.annotation.RetentionPolicy;
4741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Limimport java.util.ArrayList;
4864f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Limimport java.util.Collections;
4941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Limimport java.util.HashMap;
5017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.util.List;
5117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
5217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen/**
5317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Base class for media browse services.
5417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p>
5517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Media browse services enable applications to browse media content provided by an application
568e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim * and ask the application to start playing it. They may also be used to control content that
5717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * is already playing by way of a {@link MediaSession}.
5817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p>
5917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
6017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * To extend this class, you must declare the service in your manifest file with
6192e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik * an intent filter with the {@link #SERVICE_INTERFACE} action.
6217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
6317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * For example:
6417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p><pre>
6517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * &lt;service android:name=".MyMediaBrowserService"
6617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *          android:label="&#64;string/service_name" >
6717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *     &lt;intent-filter>
6817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *         &lt;action android:name="android.media.browse.MediaBrowserService" />
6917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *     &lt;/intent-filter>
7017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * &lt;/service>
7117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </pre>
7217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
7317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */
7417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenpublic abstract class MediaBrowserService extends Service {
7517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private static final String TAG = "MediaBrowserService";
7669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    private static final boolean DBG = false;
7769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
7869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    /**
7969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * The {@link Intent} that must be declared as handled by the service.
8069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     */
8169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    @SdkConstant(SdkConstantType.SERVICE_ACTION)
8292e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
8317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
84b1c88103fea75a19100d534e28366da0144d8138RoboErik    /**
85b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown     * A key for passing the MediaItem to the ResultReceiver in getItem.
86b1c88103fea75a19100d534e28366da0144d8138RoboErik     *
87b1c88103fea75a19100d534e28366da0144d8138RoboErik     * @hide
88b1c88103fea75a19100d534e28366da0144d8138RoboErik     */
89b1c88103fea75a19100d534e28366da0144d8138RoboErik    public static final String KEY_MEDIA_ITEM = "media_item";
90b1c88103fea75a19100d534e28366da0144d8138RoboErik
9141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001;
9241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
938e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim    /** @hide */
948e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim    @Retention(RetentionPolicy.SOURCE)
958e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim    @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED })
968e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim    private @interface ResultFlags { }
978e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim
9841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
9917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private final Handler mHandler = new Handler();
10017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private ServiceBinder mBinder;
10117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    MediaSession.Token mSession;
10217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
10317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
10417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * All the info about a connection.
10517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
10617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private class ConnectionRecord {
10717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        String pkg;
10817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        Bundle rootHints;
10917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        IMediaBrowserServiceCallbacks callbacks;
110319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        BrowserRoot root;
11141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        HashMap<String, List<Bundle>> subscriptions = new HashMap<>();
11217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
11317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
11417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
11569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * Completion handler for asynchronous callback methods in {@link MediaBrowserService}.
11669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * <p>
11769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * Each of the methods that takes one of these to send the result must call
1188e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim     * {@link #sendResult} to respond to the caller with the given results. If those
11969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * functions return without calling {@link #sendResult}, they must instead call
12069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * {@link #detach} before returning, and then may call {@link #sendResult} when
1218e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim     * they are done. If more than one of those methods is called, an exception will
12269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * be thrown.
12369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     *
12464f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * @see #onLoadChildren
12564f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * @see #onLoadItem
12617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
12769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    public class Result<T> {
12869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        private Object mDebug;
12969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        private boolean mDetachCalled;
13069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        private boolean mSendResultCalled;
1318e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim        private int mFlags;
13269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
13369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        Result(Object debug) {
13469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            mDebug = debug;
13569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
13669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
13769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        /**
13869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * Send the result back to the caller.
13969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         */
14069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        public void sendResult(T result) {
14169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            if (mSendResultCalled) {
14269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
14369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
14469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            mSendResultCalled = true;
1458e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim            onResultSent(result, mFlags);
14669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
14769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
14869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        /**
14969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * Detach this message from the current thread and allow the {@link #sendResult}
15069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * call to happen later.
15169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         */
15269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        public void detach() {
15369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            if (mDetachCalled) {
15469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                throw new IllegalStateException("detach() called when detach() had already"
15569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                        + " been called for: " + mDebug);
15669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
15769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            if (mSendResultCalled) {
15869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                throw new IllegalStateException("detach() called when sendResult() had already"
15969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                        + " been called for: " + mDebug);
16069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
16169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            mDetachCalled = true;
16269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
16369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
16469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        boolean isDone() {
16569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            return mDetachCalled || mSendResultCalled;
16669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
16769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
1688e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim        void setFlags(@ResultFlags int flags) {
1698e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim            mFlags = flags;
17041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
17141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
17269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        /**
17369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * Called when the result is sent, after assertions about not being called twice
17469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * have happened.
17569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         */
1768e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim        void onResultSent(T result, @ResultFlags int flags) {
17769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
17869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    }
17917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
18017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private class ServiceBinder extends IMediaBrowserService.Stub {
18117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
18217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        public void connect(final String pkg, final Bundle rootHints,
18317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                final IMediaBrowserServiceCallbacks callbacks) {
18417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
18517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            final int uid = Binder.getCallingUid();
18617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            if (!isValidPackage(pkg, uid)) {
18717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
18817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        + " package=" + pkg);
18917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            }
19017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
19117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
19217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    @Override
19317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    public void run() {
19417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final IBinder b = callbacks.asBinder();
19517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
1968e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim                        // Clear out the old subscriptions. We are getting new ones.
19717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        mConnections.remove(b);
19817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
19917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final ConnectionRecord connection = new ConnectionRecord();
20017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.pkg = pkg;
20117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.rootHints = rootHints;
20217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.callbacks = callbacks;
20317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
20417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
20517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
20617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        // If they didn't return something, don't allow this client.
20717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        if (connection.root == null) {
20817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            Log.i(TAG, "No root for client " + pkg + " from service "
20917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                    + getClass().getName());
21017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            try {
21117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                callbacks.onConnectFailed();
21217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            } catch (RemoteException ex) {
21317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
21417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                        + "pkg=" + pkg);
21517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            }
21617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        } else {
21717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            try {
21817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                mConnections.put(b, connection);
219d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                if (mSession != null) {
220d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                    callbacks.onConnect(connection.root.getRootId(),
221d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                            mSession, connection.root.getExtras());
222d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                }
22317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            } catch (RemoteException ex) {
22417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                Log.w(TAG, "Calling onConnect() failed. Dropping client. "
22517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                        + "pkg=" + pkg);
22617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                mConnections.remove(b);
22717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            }
22817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        }
22917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
23017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                });
23117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
23217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
23317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
23417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
23517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
23617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    @Override
23717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    public void run() {
23817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final IBinder b = callbacks.asBinder();
23917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
2408e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim                        // Clear out the old subscriptions. We are getting new ones.
24117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final ConnectionRecord old = mConnections.remove(b);
24217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        if (old != null) {
24317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            // TODO
24417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        }
24517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
24617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                });
24717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
24817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
24941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        @Override
25041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        public void addSubscription(final String id,
25141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                final IMediaBrowserServiceCallbacks callbacks) {
25241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            addSubscriptionWithOptions(id, null, callbacks);
25341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
25417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
25517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
25641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        public void addSubscriptionWithOptions(final String id, final Bundle options,
25741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                final IMediaBrowserServiceCallbacks callbacks) {
25817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
25917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    @Override
26017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    public void run() {
26117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final IBinder b = callbacks.asBinder();
26217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
26317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        // Get the record for the connection
26417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final ConnectionRecord connection = mConnections.get(b);
26517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        if (connection == null) {
266c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                            Log.w(TAG, "addSubscription for callback that isn't registered id="
267c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                                + id);
26817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            return;
26917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        }
27017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
27141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                        MediaBrowserService.this.addSubscription(id, connection, options);
27217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
27317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                });
27417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
27517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
27617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
277c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        public void removeSubscription(final String id,
27817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                final IMediaBrowserServiceCallbacks callbacks) {
27941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            removeSubscriptionWithOptions(id, null, callbacks);
28041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
28141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
28241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        @Override
28341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        public void removeSubscriptionWithOptions(final String id, final Bundle options,
28441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                final IMediaBrowserServiceCallbacks callbacks) {
28517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
28617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                @Override
28717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                public void run() {
28817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    final IBinder b = callbacks.asBinder();
28917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
29017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    ConnectionRecord connection = mConnections.get(b);
29117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    if (connection == null) {
292c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
293c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                                + id);
29417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        return;
29517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
29641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                    if (!MediaBrowserService.this.removeSubscription(id, connection, options)) {
297c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                        Log.w(TAG, "removeSubscription called for " + id
29817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                + " which is not subscribed");
29917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
30017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                }
30117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            });
30217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
303b1c88103fea75a19100d534e28366da0144d8138RoboErik
304b1c88103fea75a19100d534e28366da0144d8138RoboErik        @Override
305b1c88103fea75a19100d534e28366da0144d8138RoboErik        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
306b1c88103fea75a19100d534e28366da0144d8138RoboErik            if (TextUtils.isEmpty(mediaId) || receiver == null) {
307b1c88103fea75a19100d534e28366da0144d8138RoboErik                return;
308b1c88103fea75a19100d534e28366da0144d8138RoboErik            }
309b1c88103fea75a19100d534e28366da0144d8138RoboErik
310b1c88103fea75a19100d534e28366da0144d8138RoboErik            mHandler.post(new Runnable() {
311b1c88103fea75a19100d534e28366da0144d8138RoboErik                @Override
312b1c88103fea75a19100d534e28366da0144d8138RoboErik                public void run() {
313b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown                    performLoadItem(mediaId, receiver);
314b1c88103fea75a19100d534e28366da0144d8138RoboErik                }
315b1c88103fea75a19100d534e28366da0144d8138RoboErik            });
316b1c88103fea75a19100d534e28366da0144d8138RoboErik        }
31717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
31817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
31917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    @Override
32017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public void onCreate() {
32117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        super.onCreate();
32217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        mBinder = new ServiceBinder();
32317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
32417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
32517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    @Override
32617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public IBinder onBind(Intent intent) {
32792e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik        if (SERVICE_INTERFACE.equals(intent.getAction())) {
32817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            return mBinder;
32917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
33017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        return null;
33117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
33217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
33317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    @Override
33417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
33517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
33617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
33717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
338319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen     * Called to get the root information for browsing by a particular client.
33917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * <p>
340b1c88103fea75a19100d534e28366da0144d8138RoboErik     * The implementation should verify that the client package has permission
341b1c88103fea75a19100d534e28366da0144d8138RoboErik     * to access browse media information before returning the root id; it
342b1c88103fea75a19100d534e28366da0144d8138RoboErik     * should return null if the client is not allowed to access this
343b1c88103fea75a19100d534e28366da0144d8138RoboErik     * information.
34417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * </p>
34517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     *
346b1c88103fea75a19100d534e28366da0144d8138RoboErik     * @param clientPackageName The package name of the application which is
347b1c88103fea75a19100d534e28366da0144d8138RoboErik     *            requesting access to browse media.
348b1c88103fea75a19100d534e28366da0144d8138RoboErik     * @param clientUid The uid of the application which is requesting access to
349b1c88103fea75a19100d534e28366da0144d8138RoboErik     *            browse media.
35017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * @param rootHints An optional bundle of service-specific arguments to send
351b1c88103fea75a19100d534e28366da0144d8138RoboErik     *            to the media browse service when connecting and retrieving the
352b1c88103fea75a19100d534e28366da0144d8138RoboErik     *            root id for browsing, or null if none. The contents of this
353b1c88103fea75a19100d534e28366da0144d8138RoboErik     *            bundle may affect the information returned when browsing.
354b1c88103fea75a19100d534e28366da0144d8138RoboErik     * @return The {@link BrowserRoot} for accessing this app's content or null.
3550cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo     * @see BrowserRoot#EXTRA_RECENT
3560cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo     * @see BrowserRoot#EXTRA_OFFLINE
3570cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo     * @see BrowserRoot#EXTRA_SUGGESTED
35817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
3597e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
360319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            int clientUid, @Nullable Bundle rootHints);
36117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
36217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
36317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Called to get information about the children of a media item.
36469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * <p>
365b1c88103fea75a19100d534e28366da0144d8138RoboErik     * Implementations must call {@link Result#sendResult result.sendResult}
366b1c88103fea75a19100d534e28366da0144d8138RoboErik     * with the list of children. If loading the children will be an expensive
367b1c88103fea75a19100d534e28366da0144d8138RoboErik     * operation that should be performed on another thread,
368b1c88103fea75a19100d534e28366da0144d8138RoboErik     * {@link Result#detach result.detach} may be called before returning from
369b1c88103fea75a19100d534e28366da0144d8138RoboErik     * this function, and then {@link Result#sendResult result.sendResult}
370b1c88103fea75a19100d534e28366da0144d8138RoboErik     * called when the loading is complete.
37164f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * </p><p>
37264f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * In case the media item does not have any children, call {@link Result#sendResult}
37397243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * with an empty list. When the given {@code parentId} is invalid, implementations must
37497243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
37597243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * {@link MediaBrowser.SubscriptionCallback#onError}.
37664f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * </p>
37717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     *
378b1c88103fea75a19100d534e28366da0144d8138RoboErik     * @param parentId The id of the parent media item whose children are to be
379b1c88103fea75a19100d534e28366da0144d8138RoboErik     *            queried.
38097243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * @param result The Result to send the list of children to.
38117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
382c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik    public abstract void onLoadChildren(@NonNull String parentId,
3833625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik            @NonNull Result<List<MediaBrowser.MediaItem>> result);
38417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
38517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
38641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * Called to get information about the children of a media item.
38741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * <p>
38841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * Implementations must call {@link Result#sendResult result.sendResult}
38941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * with the list of children. If loading the children will be an expensive
39041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * operation that should be performed on another thread,
39141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * {@link Result#detach result.detach} may be called before returning from
39241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * this function, and then {@link Result#sendResult result.sendResult}
39341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * called when the loading is complete.
39464f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * </p><p>
39564f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * In case the media item does not have any children, call {@link Result#sendResult}
39697243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * with an empty list. When the given {@code parentId} is invalid, implementations must
39797243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
39897243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * {@link MediaBrowser.SubscriptionCallback#onError}.
39964f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim     * </p>
40041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *
40141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * @param parentId The id of the parent media item whose children are to be
40241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *            queried.
40397243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * @param result The Result to send the list of children to.
40441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * @param options A bundle of service-specific arguments sent from the media
40541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *            browse. The information returned through the result should be
40641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *            affected by the contents of this bundle.
40741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     */
40841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    public void onLoadChildren(@NonNull String parentId,
40941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            @NonNull Result<List<MediaBrowser.MediaItem>> result, @NonNull Bundle options) {
41041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        // To support backward compatibility, when the implementation of MediaBrowserService doesn't
41141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        // override onLoadChildren() with options, onLoadChildren() without options will be used
41241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        // instead, and the options will be applied in the implementation of result.onResultSent().
4138e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim        result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED);
41441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        onLoadChildren(parentId, result);
41541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    }
41641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
41741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    /**
418b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown     * Called to get information about a specific media item.
419b1c88103fea75a19100d534e28366da0144d8138RoboErik     * <p>
420b1c88103fea75a19100d534e28366da0144d8138RoboErik     * Implementations must call {@link Result#sendResult result.sendResult}. If
421b1c88103fea75a19100d534e28366da0144d8138RoboErik     * loading the item will be an expensive operation {@link Result#detach
422b1c88103fea75a19100d534e28366da0144d8138RoboErik     * result.detach} may be called before returning from this function, and
423b1c88103fea75a19100d534e28366da0144d8138RoboErik     * then {@link Result#sendResult result.sendResult} called when the item has
424b1c88103fea75a19100d534e28366da0144d8138RoboErik     * been loaded.
42597243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * </p><p>
42697243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * When the given {@code itemId} is invalid, implementations must call
42797243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * {@link Result#sendResult result.sendResult} with {@code null}, which will
42897243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * invoke {@link MediaBrowser.ItemCallback#onError}.
42997243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * </p><p>
43097243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * The default implementation calls {@link Result#sendResult result.sendResult}
43197243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * with {@code null}.
43297243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * </p>
433b1c88103fea75a19100d534e28366da0144d8138RoboErik     *
434b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown     * @param itemId The id for the specific
435b1c88103fea75a19100d534e28366da0144d8138RoboErik     *            {@link android.media.browse.MediaBrowser.MediaItem}.
43697243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim     * @param result The Result to send the item to.
437b1c88103fea75a19100d534e28366da0144d8138RoboErik     */
438b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown    public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) {
439b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown        result.sendResult(null);
440b1c88103fea75a19100d534e28366da0144d8138RoboErik    }
441b1c88103fea75a19100d534e28366da0144d8138RoboErik
442b1c88103fea75a19100d534e28366da0144d8138RoboErik    /**
44317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Call to set the media session.
44417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * <p>
445d64c425f32174a66a3974c63211bf457005a8d6aRoboErik     * This should be called as soon as possible during the service's startup.
446d64c425f32174a66a3974c63211bf457005a8d6aRoboErik     * It may only be called once.
447b1c88103fea75a19100d534e28366da0144d8138RoboErik     *
448b1c88103fea75a19100d534e28366da0144d8138RoboErik     * @param token The token for the service's {@link MediaSession}.
44917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
450d64c425f32174a66a3974c63211bf457005a8d6aRoboErik    public void setSessionToken(final MediaSession.Token token) {
45117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        if (token == null) {
452d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            throw new IllegalArgumentException("Session token may not be null.");
45317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
454fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik        if (mSession != null) {
455fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik            throw new IllegalStateException("The session token has already been set.");
456fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik        }
457fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik        mSession = token;
458d64c425f32174a66a3974c63211bf457005a8d6aRoboErik        mHandler.post(new Runnable() {
459d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            @Override
460d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            public void run() {
461d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                for (IBinder key : mConnections.keySet()) {
462d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    ConnectionRecord connection = mConnections.get(key);
463d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    try {
464fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik                        connection.callbacks.onConnect(connection.root.getRootId(), token,
465d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                connection.root.getExtras());
466d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    } catch (RemoteException e) {
467d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                        Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
468d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                        mConnections.remove(key);
469d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    }
470d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                }
471d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            }
472d64c425f32174a66a3974c63211bf457005a8d6aRoboErik        });
47317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
47417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
47517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
47617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Gets the session token, or null if it has not yet been created
47717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * or if it has been destroyed.
47817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
47917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public @Nullable MediaSession.Token getSessionToken() {
48017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        return mSession;
48117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
48217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
48317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
48417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Notifies all connected media browsers that the children of
485c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * the specified parent id have changed in some way.
48617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * This will cause browsers to fetch subscribed content again.
48717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     *
488c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * @param parentId The id of the parent media item whose
48917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * children changed.
49017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
49141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId) {
49241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        notifyChildrenChangedInternal(parentId, null);
49341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    }
49441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
49541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    /**
49641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * Notifies all connected media browsers that the children of
49741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * the specified parent id have changed in some way.
49841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * This will cause browsers to fetch subscribed content again.
49941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *
50041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * @param parentId The id of the parent media item whose
50141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *            children changed.
50241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * @param options A bundle of service-specific arguments to send
50341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *            to the media browse. The contents of this bundle may
50441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     *            contain the information about the change.
50541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     */
50641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
50741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        if (options == null) {
50841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
50941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
51041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        notifyChildrenChangedInternal(parentId, options);
51141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    }
51241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
51341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    private void notifyChildrenChangedInternal(final String parentId, final Bundle options) {
514c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        if (parentId == null) {
515c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
516319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
5177e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        mHandler.post(new Runnable() {
5187e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen            @Override
5197e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen            public void run() {
5207e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen                for (IBinder binder : mConnections.keySet()) {
5217e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen                    ConnectionRecord connection = mConnections.get(binder);
52241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                    List<Bundle> optionsList = connection.subscriptions.get(parentId);
52341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                    if (optionsList != null) {
52441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                        for (Bundle bundle : optionsList) {
52541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                            if (MediaBrowserUtils.hasDuplicatedItems(options, bundle)) {
52641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                                performLoadChildren(parentId, connection, bundle);
52741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                            }
52841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                        }
5297e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen                    }
530319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen                }
531319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            }
5327e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        });
53317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
53417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
53517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
53617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Return whether the given package is one of the ones that is owned by the uid.
53717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
53817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private boolean isValidPackage(String pkg, int uid) {
53917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        if (pkg == null) {
54017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            return false;
54117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
54217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        final PackageManager pm = getPackageManager();
54317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        final String[] packages = pm.getPackagesForUid(uid);
54417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        final int N = packages.length;
54517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        for (int i=0; i<N; i++) {
54617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            if (packages[i].equals(pkg)) {
54717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                return true;
54817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            }
54917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
55017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        return false;
55117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
55217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
55317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
55417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Save the subscription and if it is a new subscription send the results.
55517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
55641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    private void addSubscription(String id, ConnectionRecord connection, Bundle options) {
55717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        // Save the subscription
55841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        List<Bundle> optionsList = connection.subscriptions.get(id);
55941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        if (optionsList == null) {
56041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            optionsList = new ArrayList<>();
56141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
56241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        for (Bundle bundle : optionsList) {
56341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            if (MediaBrowserUtils.areSameOptions(options, bundle)) {
56441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                return;
56541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            }
56641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
56741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        optionsList.add(options);
56841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        connection.subscriptions.put(id, optionsList);
5697554d099e7f3a7cec166a999615245e7457bd620RoboErik        // send the results
57041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        performLoadChildren(id, connection, options);
57141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    }
57241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
57341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    /**
57441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     * Remove the subscription.
57541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim     */
57641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    private boolean removeSubscription(String id, ConnectionRecord connection, Bundle options) {
57788a03d6e62edaac715081e091da7e9abc21d2c0dSungsoo Lim        if (options == null) {
57888a03d6e62edaac715081e091da7e9abc21d2c0dSungsoo Lim            return connection.subscriptions.remove(id) != null;
57988a03d6e62edaac715081e091da7e9abc21d2c0dSungsoo Lim        }
58041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        boolean removed = false;
58141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        List<Bundle> optionsList = connection.subscriptions.get(id);
58241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        if (optionsList != null) {
58341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            for (Bundle bundle : optionsList) {
58441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                if (MediaBrowserUtils.areSameOptions(options, bundle)) {
58541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                    removed = true;
58641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                    optionsList.remove(bundle);
58741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                    break;
58841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                }
58941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            }
59041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            if (optionsList.size() == 0) {
59141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                connection.subscriptions.remove(id);
59241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            }
59341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
59441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        return removed;
59517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
59617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
59717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
59817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Call onLoadChildren and then send the results back to the connection.
59917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * <p>
60017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Callers must make sure that this connection is still connected.
60117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
60241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    private void performLoadChildren(final String parentId, final ConnectionRecord connection,
60341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            final Bundle options) {
6043625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        final Result<List<MediaBrowser.MediaItem>> result
605c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                = new Result<List<MediaBrowser.MediaItem>>(parentId) {
60669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            @Override
6078e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim            void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
60869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
60969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    if (DBG) {
61069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
611c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
61269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    }
61369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    return;
61469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                }
61569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
61641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                List<MediaBrowser.MediaItem> filteredList =
61741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                        (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
61841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                        ? applyOptions(list, options) : list;
6192189f58ec5a4fa35b7e06d9d28abcef5d67f9c7fSungsoo Lim                final ParceledListSlice<MediaBrowser.MediaItem> pls =
62041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim                        filteredList == null ? null : new ParceledListSlice<>(filteredList);
62169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                try {
622d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                    // NOTE: Do not call onLoadChildrenWithOptions when options are null. Otherwise,
623d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                    // it will break the action of support library which expects onLoadChildren will
624d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                    // be called when options are null.
625d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                    if (options == null) {
626d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                        connection.callbacks.onLoadChildren(parentId, pls);
627d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                    } else {
628d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                        connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options);
629d67933e271e3329204827e4cc16b7dc562dbfad1Sungsoo Lim                    }
63069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                } catch (RemoteException ex) {
63169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    // The other side is in the process of crashing.
632c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
63369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                            + " package=" + connection.pkg);
63469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                }
63569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
63669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        };
63769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
63841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        if (options == null) {
63941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            onLoadChildren(parentId, result);
64041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        } else {
64141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            onLoadChildren(parentId, result, options);
64241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
64369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
64469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        if (!result.isDone()) {
64569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
646c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    + " before returning for package=" + connection.pkg + " id=" + parentId);
64717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
64817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
649319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen
65041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    private List<MediaBrowser.MediaItem> applyOptions(List<MediaBrowser.MediaItem> list,
65141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            final Bundle options) {
65264f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim        if (list == null) {
65364f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim            return null;
65464f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim        }
65541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1);
65641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1);
65741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        if (page == -1 && pageSize == -1) {
65841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            return list;
65941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
66041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        int fromIndex = pageSize * (page - 1);
66141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        int toIndex = fromIndex + pageSize;
66241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        if (page < 1 || pageSize < 1 || fromIndex >= list.size()) {
66364f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim            return Collections.EMPTY_LIST;
66441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
66541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        if (toIndex > list.size()) {
66641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim            toIndex = list.size();
66741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        }
66841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim        return list.subList(fromIndex, toIndex);
66941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim    }
67041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim
671b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown    private void performLoadItem(String itemId, final ResultReceiver receiver) {
672b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown        final Result<MediaBrowser.MediaItem> result =
673b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown                new Result<MediaBrowser.MediaItem>(itemId) {
674b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown            @Override
6758e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim            void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
676b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown                Bundle bundle = new Bundle();
677b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown                bundle.putParcelable(KEY_MEDIA_ITEM, item);
678b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown                receiver.send(0, bundle);
679b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown            }
680b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown        };
681b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown
682b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown        MediaBrowserService.this.onLoadItem(itemId, result);
683b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown
684b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown        if (!result.isDone()) {
685b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
686b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown                    + " before returning for id=" + itemId);
687b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown        }
688b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown    }
689b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown
6907e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen    /**
6917e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen     * Contains information that the browser service needs to send to the client
6927e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen     * when first connected.
6937e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen     */
6947e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen    public static final class BrowserRoot {
6950cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo        /**
6960cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
6970cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * browser root for recently played media items.
6980cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
6990cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
7000cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * supplied as a root hint for retrieving media items that are recently played.
7010cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * If the media browser service can provide such media items, the implementation must return
7020cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
7030cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7040cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * <p>The root hint may contain multiple keys.
7050cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7060cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * @see #EXTRA_OFFLINE
7070cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * @see #EXTRA_SUGGESTED
7080cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         */
7090cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo        public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
7100cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo
7110cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo        /**
7120cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
7130cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * browser root for offline media items.
7140cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7150cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
7160cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * supplied as a root hint for retrieving media items that are can be played without an
7170cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * internet connection.
7180cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * If the media browser service can provide such media items, the implementation must return
7190cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
7200cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7210cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * <p>The root hint may contain multiple keys.
7220cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7230cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * @see #EXTRA_RECENT
7240cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * @see #EXTRA_SUGGESTED
7250cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         */
7260cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo        public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
7270cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo
7280cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo        /**
7290cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * The lookup key for a boolean that indicates whether the browser service should return a
7300cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * browser root for suggested media items.
7310cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7320cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * <p>When creating a media browser for a given media browser service, this key can be
7330cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * supplied as a root hint for retrieving the media items suggested by the media browser
7340cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
7350cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * is considered ordered by relevance, first being the top suggestion.
7360cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * If the media browser service can provide such media items, the implementation must return
7370cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
7380cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7390cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * <p>The root hint may contain multiple keys.
7400cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         *
7410cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * @see #EXTRA_RECENT
7420cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         * @see #EXTRA_OFFLINE
7430cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo         */
7440cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo        public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
7450cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo
746c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        final private String mRootId;
747319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        final private Bundle mExtras;
7487e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen
7497e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        /**
7507e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         * Constructs a browser root.
751c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik         * @param rootId The root id for browsing.
7527e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         * @param extras Any extras about the browser service.
7537e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         */
754c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
755c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            if (rootId == null) {
756c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
757319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen                        "Use null for BrowserRoot instead.");
758319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            }
759c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            mRootId = rootId;
760319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            mExtras = extras;
761319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
762319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen
7637e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        /**
764c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik         * Gets the root id for browsing.
7657e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         */
766c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        public String getRootId() {
767c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            return mRootId;
768319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
769319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen
7707e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        /**
771082e873999e293397703fb511ff2edbab306f180Jae Seo         * Gets any extras about the browser service.
7727e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         */
7737e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        public Bundle getExtras() {
774319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            return mExtras;
775319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
776319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen    }
77717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen}
778