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
1917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport 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;
2917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.media.session.MediaSession;
3017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Binder;
3117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Bundle;
3217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.IBinder;
3317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.Handler;
3417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.os.RemoteException;
353625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.service.media.IMediaBrowserService;
363625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErikimport android.service.media.IMediaBrowserServiceCallbacks;
3717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.util.ArrayMap;
3817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport android.util.Log;
3917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
4017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.io.FileDescriptor;
4117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.io.PrintWriter;
4217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.util.HashSet;
4317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.util.List;
4417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
4517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen/**
4617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Base class for media browse services.
4717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p>
4817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Media browse services enable applications to browse media content provided by an application
4917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * and ask the application to start playing it.  They may also be used to control content that
5017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * is already playing by way of a {@link MediaSession}.
5117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p>
5217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
5317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * To extend this class, you must declare the service in your manifest file with
5492e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik * an intent filter with the {@link #SERVICE_INTERFACE} action.
5517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
5617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * For example:
5717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p><pre>
5817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * &lt;service android:name=".MyMediaBrowserService"
5917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *          android:label="&#64;string/service_name" >
6017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *     &lt;intent-filter>
6117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *         &lt;action android:name="android.media.browse.MediaBrowserService" />
6217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *     &lt;/intent-filter>
6317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * &lt;/service>
6417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </pre>
6517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen *
6617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */
6717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenpublic abstract class MediaBrowserService extends Service {
6817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private static final String TAG = "MediaBrowserService";
6969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    private static final boolean DBG = false;
7069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
7169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    /**
7269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * The {@link Intent} that must be declared as handled by the service.
7369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     */
7469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    @SdkConstant(SdkConstantType.SERVICE_ACTION)
7592e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
7617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
7717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
7817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private final Handler mHandler = new Handler();
7917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private ServiceBinder mBinder;
8017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    MediaSession.Token mSession;
8117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
8217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
8317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * All the info about a connection.
8417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
8517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private class ConnectionRecord {
8617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        String pkg;
8717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        Bundle rootHints;
8817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        IMediaBrowserServiceCallbacks callbacks;
89319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        BrowserRoot root;
90c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        HashSet<String> subscriptions = new HashSet();
9117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
9217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
9317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
9469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * Completion handler for asynchronous callback methods in {@link MediaBrowserService}.
9569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * <p>
9669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * Each of the methods that takes one of these to send the result must call
9769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * {@link #sendResult} to respond to the caller with the given results.  If those
9869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * functions return without calling {@link #sendResult}, they must instead call
9969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * {@link #detach} before returning, and then may call {@link #sendResult} when
10069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * they are done.  If more than one of those methods is called, an exception will
10169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * be thrown.
10269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     *
10369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * @see MediaBrowserService#onLoadChildren
10417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
10569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    public class Result<T> {
10669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        private Object mDebug;
10769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        private boolean mDetachCalled;
10869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        private boolean mSendResultCalled;
10969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
11069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        Result(Object debug) {
11169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            mDebug = debug;
11269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
11369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
11469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        /**
11569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * Send the result back to the caller.
11669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         */
11769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        public void sendResult(T result) {
11869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            if (mSendResultCalled) {
11969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
12069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
12169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            mSendResultCalled = true;
12269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            onResultSent(result);
12369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
12469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
12569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        /**
12669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * Detach this message from the current thread and allow the {@link #sendResult}
12769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * call to happen later.
12869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         */
12969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        public void detach() {
13069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            if (mDetachCalled) {
13169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                throw new IllegalStateException("detach() called when detach() had already"
13269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                        + " been called for: " + mDebug);
13369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
13469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            if (mSendResultCalled) {
13569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                throw new IllegalStateException("detach() called when sendResult() had already"
13669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                        + " been called for: " + mDebug);
13769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
13869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            mDetachCalled = true;
13969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
14069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
14169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        boolean isDone() {
14269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            return mDetachCalled || mSendResultCalled;
14369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
14469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
14569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        /**
14669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * Called when the result is sent, after assertions about not being called twice
14769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         * have happened.
14869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato         */
14969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        void onResultSent(T result) {
15069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        }
15169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato    }
15217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
15317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private class ServiceBinder extends IMediaBrowserService.Stub {
15417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
15517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        public void connect(final String pkg, final Bundle rootHints,
15617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                final IMediaBrowserServiceCallbacks callbacks) {
15717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
15817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            final int uid = Binder.getCallingUid();
15917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            if (!isValidPackage(pkg, uid)) {
16017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
16117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        + " package=" + pkg);
16217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            }
16317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
16417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
16517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    @Override
16617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    public void run() {
16717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final IBinder b = callbacks.asBinder();
16817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
16917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        // Clear out the old subscriptions.  We are getting new ones.
17017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        mConnections.remove(b);
17117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
17217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final ConnectionRecord connection = new ConnectionRecord();
17317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.pkg = pkg;
17417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.rootHints = rootHints;
17517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.callbacks = callbacks;
17617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
17717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
17817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
17917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        // If they didn't return something, don't allow this client.
18017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        if (connection.root == null) {
18117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            Log.i(TAG, "No root for client " + pkg + " from service "
18217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                    + getClass().getName());
18317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            try {
18417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                callbacks.onConnectFailed();
18517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            } catch (RemoteException ex) {
18617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
18717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                        + "pkg=" + pkg);
18817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            }
18917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        } else {
19017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            try {
19117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                mConnections.put(b, connection);
192d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                if (mSession != null) {
193d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                    callbacks.onConnect(connection.root.getRootId(),
194d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                            mSession, connection.root.getExtras());
195d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                }
19617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            } catch (RemoteException ex) {
19717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                Log.w(TAG, "Calling onConnect() failed. Dropping client. "
19817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                        + "pkg=" + pkg);
19917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                mConnections.remove(b);
20017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            }
20117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        }
20217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
20317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                });
20417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
20517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
20617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
20717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
20817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
20917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    @Override
21017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    public void run() {
21117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final IBinder b = callbacks.asBinder();
21217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
21317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        // Clear out the old subscriptions.  We are getting new ones.
21417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final ConnectionRecord old = mConnections.remove(b);
21517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        if (old != null) {
21617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            // TODO
21717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        }
21817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
21917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                });
22017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
22117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
22217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
22317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
224c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        public void addSubscription(final String id, final IMediaBrowserServiceCallbacks callbacks) {
22517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
22617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    @Override
22717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    public void run() {
22817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final IBinder b = callbacks.asBinder();
22917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
23017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        // Get the record for the connection
23117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        final ConnectionRecord connection = mConnections.get(b);
23217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        if (connection == null) {
233c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                            Log.w(TAG, "addSubscription for callback that isn't registered id="
234c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                                + id);
23517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                            return;
23617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        }
23717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
238c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                        MediaBrowserService.this.addSubscription(id, connection);
23917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
24017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                });
24117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
24217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
24317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        @Override
244c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        public void removeSubscription(final String id,
24517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                final IMediaBrowserServiceCallbacks callbacks) {
24617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            mHandler.post(new Runnable() {
24717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                @Override
24817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                public void run() {
24917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    final IBinder b = callbacks.asBinder();
25017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
25117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    ConnectionRecord connection = mConnections.get(b);
25217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    if (connection == null) {
253c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
254c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                                + id);
25517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                        return;
25617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
257c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    if (!connection.subscriptions.remove(id)) {
258c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                        Log.w(TAG, "removeSubscription called for " + id
25917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                                + " which is not subscribed");
26017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                    }
26117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                }
26217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            });
26317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
26417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
26517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
26617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    @Override
26717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public void onCreate() {
26817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        super.onCreate();
26917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        mBinder = new ServiceBinder();
27017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
27117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
27217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    @Override
27317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public IBinder onBind(Intent intent) {
27492e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik        if (SERVICE_INTERFACE.equals(intent.getAction())) {
27517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            return mBinder;
27617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
27717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        return null;
27817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
27917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
28017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    @Override
28117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
28217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
28317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
28417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
285319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen     * Called to get the root information for browsing by a particular client.
28617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * <p>
28717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * The implementation should verify that the client package has
28817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * permission to access browse media information before returning
289c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * the root id; it should return null if the client is not
29017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * allowed to access this information.
29117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * </p>
29217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     *
29317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * @param clientPackageName The package name of the application
29417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * which is requesting access to browse media.
29517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * @param clientUid The uid of the application which is requesting
29617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * access to browse media.
29717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * @param rootHints An optional bundle of service-specific arguments to send
298c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * to the media browse service when connecting and retrieving the root id
29917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * for browsing, or null if none.  The contents of this bundle may affect
30017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * the information returned when browsing.
30117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
3027e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
303319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            int clientUid, @Nullable Bundle rootHints);
30417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
30517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
30617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Called to get information about the children of a media item.
30769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * <p>
30869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * Implementations must call result.{@link Result#sendResult result.sendResult} with the list
30969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * of children. If loading the children will be an expensive operation that should be performed
31069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * on another thread, result.{@link Result#detach result.detach} may be called before returning
31169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * from this function, and then {@link Result#sendResult result.sendResult} called when
31269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato     * the loading is complete.
31317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     *
314c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * @param parentId The id of the parent media item whose
31517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * children are to be queried.
316c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * @return The list of children, or null if the id is invalid.
31717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
318c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik    public abstract void onLoadChildren(@NonNull String parentId,
3193625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik            @NonNull Result<List<MediaBrowser.MediaItem>> result);
32017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
32117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
32217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Call to set the media session.
32317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * <p>
324d64c425f32174a66a3974c63211bf457005a8d6aRoboErik     * This should be called as soon as possible during the service's startup.
325d64c425f32174a66a3974c63211bf457005a8d6aRoboErik     * It may only be called once.
32617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
327d64c425f32174a66a3974c63211bf457005a8d6aRoboErik    public void setSessionToken(final MediaSession.Token token) {
32817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        if (token == null) {
329d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            throw new IllegalArgumentException("Session token may not be null.");
33017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
331fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik        if (mSession != null) {
332fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik            throw new IllegalStateException("The session token has already been set.");
333fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik        }
334fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik        mSession = token;
335d64c425f32174a66a3974c63211bf457005a8d6aRoboErik        mHandler.post(new Runnable() {
336d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            @Override
337d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            public void run() {
338d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                for (IBinder key : mConnections.keySet()) {
339d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    ConnectionRecord connection = mConnections.get(key);
340d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    try {
341fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik                        connection.callbacks.onConnect(connection.root.getRootId(), token,
342d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                                connection.root.getExtras());
343d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    } catch (RemoteException e) {
344d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                        Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
345d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                        mConnections.remove(key);
346d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                    }
347d64c425f32174a66a3974c63211bf457005a8d6aRoboErik                }
348d64c425f32174a66a3974c63211bf457005a8d6aRoboErik            }
349d64c425f32174a66a3974c63211bf457005a8d6aRoboErik        });
35017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
35117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
35217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
35317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Gets the session token, or null if it has not yet been created
35417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * or if it has been destroyed.
35517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
35617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    public @Nullable MediaSession.Token getSessionToken() {
35717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        return mSession;
35817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
35917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
36017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
36117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Notifies all connected media browsers that the children of
362c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * the specified parent id have changed in some way.
36317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * This will cause browsers to fetch subscribed content again.
36417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     *
365c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik     * @param parentId The id of the parent media item whose
36617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * children changed.
36717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
368c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik    public void notifyChildrenChanged(@NonNull final String parentId) {
369c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        if (parentId == null) {
370c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
371319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
3727e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        mHandler.post(new Runnable() {
3737e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen            @Override
3747e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen            public void run() {
3757e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen                for (IBinder binder : mConnections.keySet()) {
3767e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen                    ConnectionRecord connection = mConnections.get(binder);
377c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    if (connection.subscriptions.contains(parentId)) {
378c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                        performLoadChildren(parentId, connection);
3797e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen                    }
380319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen                }
381319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            }
3827e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        });
38317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
38417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
38517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
38617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Return whether the given package is one of the ones that is owned by the uid.
38717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
38817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    private boolean isValidPackage(String pkg, int uid) {
38917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        if (pkg == null) {
39017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            return false;
39117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
39217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        final PackageManager pm = getPackageManager();
39317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        final String[] packages = pm.getPackagesForUid(uid);
39417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        final int N = packages.length;
39517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        for (int i=0; i<N; i++) {
39617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            if (packages[i].equals(pkg)) {
39717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen                return true;
39817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen            }
39917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
40017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        return false;
40117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
40217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
40317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
40417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Save the subscription and if it is a new subscription send the results.
40517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
406c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik    private void addSubscription(String id, ConnectionRecord connection) {
40717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        // Save the subscription
408c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        final boolean added = connection.subscriptions.add(id);
40917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
41017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        // If this is a new subscription, send the results
41117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        if (added) {
412c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            performLoadChildren(id, connection);
41317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
41417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
41517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen
41617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    /**
41717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Call onLoadChildren and then send the results back to the connection.
41817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * <p>
41917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     * Callers must make sure that this connection is still connected.
42017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen     */
421c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik    private void performLoadChildren(final String parentId, final ConnectionRecord connection) {
4223625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik        final Result<List<MediaBrowser.MediaItem>> result
423c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                = new Result<List<MediaBrowser.MediaItem>>(parentId) {
42469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            @Override
4253625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik            void onResultSent(List<MediaBrowser.MediaItem> list) {
42669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                if (list == null) {
427c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    throw new IllegalStateException("onLoadChildren sent null list for id "
428c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                            + parentId);
42969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                }
43069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
43169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    if (DBG) {
43269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
433c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
43469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    }
43569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    return;
43669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                }
43769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
4383625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik                final ParceledListSlice<MediaBrowser.MediaItem> pls = new ParceledListSlice(list);
43969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                try {
440c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    connection.callbacks.onLoadChildren(parentId, pls);
44169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                } catch (RemoteException ex) {
44269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                    // The other side is in the process of crashing.
443c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
44469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                            + " package=" + connection.pkg);
44569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato                }
44669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            }
44769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        };
44869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
449c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        onLoadChildren(parentId, result);
45069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato
45169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato        if (!result.isDone()) {
45269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
453c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                    + " before returning for package=" + connection.pkg + " id=" + parentId);
45417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen        }
45517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen    }
456319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen
4577e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen    /**
4587e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen     * Contains information that the browser service needs to send to the client
4597e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen     * when first connected.
4607e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen     */
4617e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen    public static final class BrowserRoot {
462c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        final private String mRootId;
463319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        final private Bundle mExtras;
4647e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen
4657e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        /**
4667e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         * Constructs a browser root.
467c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik         * @param rootId The root id for browsing.
4687e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         * @param extras Any extras about the browser service.
4697e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         */
470c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
471c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            if (rootId == null) {
472c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
473319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen                        "Use null for BrowserRoot instead.");
474319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            }
475c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            mRootId = rootId;
476319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            mExtras = extras;
477319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
478319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen
4797e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        /**
480c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik         * Gets the root id for browsing.
4817e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         */
482c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik        public String getRootId() {
483c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik            return mRootId;
484319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
485319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen
4867e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        /**
4877e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         * Gets any extras about the brwoser service.
4887e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen         */
4897e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen        public Bundle getExtras() {
490319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen            return mExtras;
491319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen        }
492319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen    }
49317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen}
494