MediaBrowserServiceCompat.java revision 6a4150834d679d66abef50aff74d30ae2e846a32
1e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/*
2e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Copyright (C) 2015 The Android Open Source Project
3e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
4e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Licensed under the Apache License, Version 2.0 (the "License");
5e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * you may not use this file except in compliance with the License.
6e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * You may obtain a copy of the License at
7e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
8e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *      http://www.apache.org/licenses/LICENSE-2.0
9e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
10e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Unless required by applicable law or agreed to in writing, software
11e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * distributed under the License is distributed on an "AS IS" BASIS,
12e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * See the License for the specific language governing permissions and
14e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * limitations under the License.
15e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */
16e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
17e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seopackage android.support.v4.media;
18e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
19e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.app.Service;
20e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.Intent;
21e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.pm.PackageManager;
22e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Binder;
236a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Limimport android.os.Build;
24e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Bundle;
25e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Handler;
26e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.IBinder;
276a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Limimport android.os.Parcel;
28e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.RemoteException;
29e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.NonNull;
30e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.Nullable;
31e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.media.session.MediaSessionCompat;
32e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.os.ResultReceiver;
33e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.util.ArrayMap;
34e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.text.TextUtils;
35e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.util.Log;
36e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
37e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.FileDescriptor;
38e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.PrintWriter;
396a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Limimport java.util.ArrayList;
40e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.util.HashSet;
41e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.util.List;
42e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
43e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/**
44e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Base class for media browse services.
45e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p>
46e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Media browse services enable applications to browse media content provided by an application
47e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * and ask the application to start playing it.  They may also be used to control content that
48096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * is already playing by way of a {@link MediaSessionCompat}.
49e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p>
50e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
51e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * To extend this class, you must declare the service in your manifest file with
52e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * an intent filter with the {@link #SERVICE_INTERFACE} action.
53e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
54e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * For example:
55e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p><pre>
56096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * &lt;service android:name=".MyMediaBrowserServiceCompat"
57e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *          android:label="&#64;string/service_name" >
58e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;intent-filter>
596a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim *         &lt;action android:name="android.media.browse.MediaBrowserService" />
60e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;/intent-filter>
61e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * &lt;/service>
62e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </pre>
63e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @hide
64e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */
65096f2531cb790bc1106377d2da344614a3b88d39Jae Seopublic abstract class MediaBrowserServiceCompat extends Service {
66096f2531cb790bc1106377d2da344614a3b88d39Jae Seo    private static final String TAG = "MediaBrowserServiceCompat";
67e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private static final boolean DBG = false;
68e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
696a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    private MediaBrowserServiceImpl mImpl;
706a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
71e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
72e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The {@link Intent} that must be declared as handled by the service.
73e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
746a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
75e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
76e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
77e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * A key for passing the MediaItem to the ResultReceiver in getItem.
78e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
79e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @hide
80e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
81e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final String KEY_MEDIA_ITEM = "media_item";
82e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
83e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
84e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private final Handler mHandler = new Handler();
85e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    MediaSessionCompat.Token mSession;
86e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
876a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    interface MediaBrowserServiceImpl {
886a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onCreate();
896a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        IBinder onBind(Intent intent);
906a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
916a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
926a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl {
936a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        private ServiceBinderCompat mBinder;
946a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
956a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
966a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onCreate() {
976a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mBinder = new ServiceBinderCompat(new ServiceStub());
986a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
996a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
1006a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
1016a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public IBinder onBind(Intent intent) {
1026a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            // STOPSHIP: Use messenger or version management for further extension
1036a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            if (SERVICE_INTERFACE.equals(intent.getAction())) {
1046a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                return mBinder;
1056a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            }
1066a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            return null;
1076a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
1086a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
1096a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
1106a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl {
1116a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        private Object mServiceObj;
1126a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
1136a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
1146a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onCreate() {
1156a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi21.createService();
1166a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj,
1176a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    new ServiceStubApi21(new ServiceStub()));
1186a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
1196a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
1206a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
1216a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public IBinder onBind(Intent intent) {
1226a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent);
1236a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
1246a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
1256a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
126e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
127e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * All the info about a connection.
128e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
129e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private class ConnectionRecord {
130e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        String pkg;
131e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Bundle rootHints;
1326a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        ServiceCallbacks callbacks;
133e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        BrowserRoot root;
134e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        HashSet<String> subscriptions = new HashSet();
135e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
136e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
137e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
138096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}.
139e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
140e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Each of the methods that takes one of these to send the result must call
141e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link #sendResult} to respond to the caller with the given results.  If those
142e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * functions return without calling {@link #sendResult}, they must instead call
143e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link #detach} before returning, and then may call {@link #sendResult} when
144e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * they are done.  If more than one of those methods is called, an exception will
145e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * be thrown.
146e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
147096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadChildren
148096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadItem
149e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
1506a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    public static class Result<T> {
151e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private Object mDebug;
152e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mDetachCalled;
153e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mSendResultCalled;
154e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
155e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Result(Object debug) {
156e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDebug = debug;
157e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
158e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
159e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
160e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Send the result back to the caller.
161e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
162e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void sendResult(T result) {
163e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
164e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
165e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
166e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mSendResultCalled = true;
167e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            onResultSent(result);
168e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
169e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
170e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
171e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Detach this message from the current thread and allow the {@link #sendResult}
172e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * call to happen later.
173e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
174e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void detach() {
175e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mDetachCalled) {
176e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when detach() had already"
177e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
178e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
179e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
180e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when sendResult() had already"
181e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
182e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
183e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDetachCalled = true;
184e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
185e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
186e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        boolean isDone() {
187e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mDetachCalled || mSendResultCalled;
188e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
189e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
190e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
191e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Called when the result is sent, after assertions about not being called twice
192e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * have happened.
193e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
194e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        void onResultSent(T result) {
195e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
196e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
197e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
1986a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    private class ServiceStub {
199e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void connect(final String pkg, final Bundle rootHints,
2006a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                final ServiceCallbacks callbacks) {
201e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
202e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            final int uid = Binder.getCallingUid();
203e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (!isValidPackage(pkg, uid)) {
204e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
205e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " package=" + pkg);
206e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
207e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
208e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mHandler.post(new Runnable() {
2096a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                @Override
2106a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                public void run() {
2116a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    final IBinder b = callbacks.asBinder();
2126a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
2136a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    // Clear out the old subscriptions.  We are getting new ones.
2146a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    mConnections.remove(b);
2156a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
2166a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
2176a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    connection.pkg = pkg;
2186a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    connection.rootHints = rootHints;
2196a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    connection.callbacks = callbacks;
2206a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
2216a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    connection.root =
2226a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                            MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints);
2236a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
2246a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    // If they didn't return something, don't allow this client.
2256a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    if (connection.root == null) {
2266a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        Log.i(TAG, "No root for client " + pkg + " from service "
2276a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                                + getClass().getName());
2286a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        try {
2296a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                            callbacks.onConnectFailed();
2306a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        } catch (RemoteException ex) {
2316a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                            Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
2326a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                                    + "pkg=" + pkg);
2336a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        }
2346a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    } else {
2356a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        try {
2366a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                            mConnections.put(b, connection);
2376a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                            if (mSession != null) {
2386a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                                callbacks.onConnect(connection.root.getRootId(),
2396a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                                        mSession, connection.root.getExtras());
240e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            }
2416a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        } catch (RemoteException ex) {
2426a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                            Log.w(TAG, "Calling onConnect() failed. Dropping client. "
2436a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                                    + "pkg=" + pkg);
2446a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                            mConnections.remove(b);
245e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        }
246e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
2476a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                }
2486a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            });
249e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
250e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
2516a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void disconnect(final ServiceCallbacks callbacks) {
252e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mHandler.post(new Runnable() {
2536a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                @Override
2546a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                public void run() {
2556a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    final IBinder b = callbacks.asBinder();
2566a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
2576a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    // Clear out the old subscriptions.  We are getting new ones.
2586a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    final ConnectionRecord old = mConnections.remove(b);
2596a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    if (old != null) {
2606a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        // TODO
261e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
2626a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                }
2636a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            });
264e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
265e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
266e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
2676a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void addSubscription(final String id, final ServiceCallbacks callbacks) {
268e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mHandler.post(new Runnable() {
2696a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                @Override
2706a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                public void run() {
2716a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    final IBinder b = callbacks.asBinder();
272e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
2736a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    // Get the record for the connection
2746a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    final ConnectionRecord connection = mConnections.get(b);
2756a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    if (connection == null) {
2766a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        Log.w(TAG, "addSubscription for callback that isn't registered id="
2776a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                                + id);
2786a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                        return;
279e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
2806a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
2816a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    MediaBrowserServiceCompat.this.addSubscription(id, connection);
2826a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                }
2836a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            });
284e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
285e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
2866a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void removeSubscription(final String id, final ServiceCallbacks callbacks) {
287e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mHandler.post(new Runnable() {
288e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
289e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
290e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    final IBinder b = callbacks.asBinder();
291e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
292e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(b);
293e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (connection == null) {
294e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
295e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + id);
296e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        return;
297e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
298e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (!connection.subscriptions.remove(id)) {
299e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription called for " + id
300e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " which is not subscribed");
301e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
302e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
303e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
304e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
305e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
306e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
307e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (TextUtils.isEmpty(mediaId) || receiver == null) {
308e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return;
309e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
310e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
311e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mHandler.post(new Runnable() {
312e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
313e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
314e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    performLoadItem(mediaId, receiver);
315e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
316e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
317e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
318e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
319e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
3206a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    private class ServiceBinderCompat extends IMediaBrowserServiceCompat.Stub {
3216a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        final ServiceStub mServiceStub;
3226a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3236a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        ServiceBinderCompat(ServiceStub binder) {
3246a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mServiceStub = binder;
3256a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3266a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3276a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3286a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void connect(final String pkg, final Bundle rootHints,
3296a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                final IMediaBrowserServiceCompatCallbacks callbacks) {
3306a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mServiceStub.connect(pkg, rootHints, new ServiceCallbacksCompat(callbacks));
3316a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3326a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3336a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3346a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void disconnect(final IMediaBrowserServiceCompatCallbacks callbacks) {
3356a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mConnections.get(callbacks.asBinder());
3366a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mServiceStub.disconnect(new ServiceCallbacksCompat(callbacks));
3376a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3386a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3396a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3406a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3416a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void addSubscription(
3426a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                final String id, final IMediaBrowserServiceCompatCallbacks callbacks) {
3436a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mServiceStub.addSubscription(id, new ServiceCallbacksCompat(callbacks));
3446a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3456a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3466a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3476a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void removeSubscription(final String id,
3486a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                final IMediaBrowserServiceCompatCallbacks callbacks) {
3496a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mServiceStub.removeSubscription(id, new ServiceCallbacksCompat(callbacks));
3506a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3516a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3526a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3536a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
3546a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mServiceStub.getMediaItem(mediaId, receiver);
3556a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3566a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
3576a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3586a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    private class ServiceStubApi21 implements MediaBrowserServiceCompatApi21.ServiceStub {
3596a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        final ServiceStub mBinder;
3606a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3616a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        ServiceStubApi21(ServiceStub binder) {
3626a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mBinder = binder;
3636a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3646a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3656a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3666a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void connect(final String pkg, final Bundle rootHints,
3676a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
3686a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mBinder.connect(pkg, rootHints, new ServiceCallbacksApi21(callbacks));
3696a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3706a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3716a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3726a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void disconnect(final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
3736a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mBinder.disconnect(new ServiceCallbacksApi21(callbacks));
3746a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3756a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3766a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3776a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3786a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void addSubscription(
3796a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                final String id, final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
3806a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mBinder.addSubscription(id, new ServiceCallbacksApi21(callbacks));
3816a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3826a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3836a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3846a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void removeSubscription(final String id,
3856a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
3866a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mBinder.removeSubscription(id, new ServiceCallbacksApi21(callbacks));
3876a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3886a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3896a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        @Override
3906a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void getMediaItem(final String mediaId, final android.os.ResultReceiver receiver) {
3916a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            ResultReceiver receiverCompat = new ResultReceiver(mHandler) {
3926a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                @Override
3936a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                protected void onReceiveResult(int resultCode, Bundle resultData) {
3946a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    receiver.send(resultCode, resultData);
3956a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                }
3966a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            };
3976a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mBinder.getMediaItem(mediaId, receiverCompat);
3986a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
3996a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
4006a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4016a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    private interface ServiceCallbacks {
4026a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        IBinder asBinder();
4036a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
4046a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                throws RemoteException;
4056a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        void onConnectFailed() throws RemoteException;
4066a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
4076a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                throws RemoteException;
4086a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
4096a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4106a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    private class ServiceCallbacksCompat implements ServiceCallbacks {
4116a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        final IMediaBrowserServiceCompatCallbacks mCallbacks;
4126a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4136a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        ServiceCallbacksCompat(IMediaBrowserServiceCompatCallbacks callbacks) {
4146a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks = callbacks;
4156a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4166a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4176a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public IBinder asBinder() {
4186a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            return mCallbacks.asBinder();
4196a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4206a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4216a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
4226a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                throws RemoteException {
4236a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks.onConnect(root, session, extras);
4246a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4256a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4266a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onConnectFailed() throws RemoteException {
4276a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks.onConnectFailed();
4286a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4296a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4306a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
4316a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                throws RemoteException {
4326a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks.onLoadChildren(mediaId, list);
4336a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4346a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
4356a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4366a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    private class ServiceCallbacksApi21 implements ServiceCallbacks {
4376a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        final MediaBrowserServiceCompatApi21.ServiceCallbacks mCallbacks;
4386a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4396a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        ServiceCallbacksApi21(MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
4406a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks = callbacks;
4416a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4426a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4436a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public IBinder asBinder() {
4446a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            return mCallbacks.asBinder();
4456a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4466a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4476a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
4486a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                throws RemoteException {
4496a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks.onConnect(root, session.getToken(), extras);
4506a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4516a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4526a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onConnectFailed() throws RemoteException {
4536a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks.onConnectFailed();
4546a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4556a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
4566a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
4576a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                throws RemoteException {
4586a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            List<Parcel> parcelList = null;
4596a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            if (list != null) {
4606a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                parcelList = new ArrayList<>();
4616a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                for (MediaBrowserCompat.MediaItem item : list) {
4626a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    Parcel parcel = Parcel.obtain();
4636a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    item.writeToParcel(parcel, 0);
4646a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                    parcelList.add(parcel);
4656a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim                }
4666a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            }
4676a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mCallbacks.onLoadChildren(mediaId, parcelList);
4686a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4696a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim    }
4706a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
471e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
472e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void onCreate() {
473e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        super.onCreate();
4746a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        if (Build.VERSION.SDK_INT >= 21) {
4756a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mImpl = new MediaBrowserServiceImplApi21();
4766a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        } else {
4776a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim            mImpl = new MediaBrowserServiceImplBase();
4786a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        }
4796a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        mImpl.onCreate();
480e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
481e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
482e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
483e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public IBinder onBind(Intent intent) {
4846a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim        return mImpl.onBind(intent);
485e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
486e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
487e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
488e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
489e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
490e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
491e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
492e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get the root information for browsing by a particular client.
493e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
494e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The implementation should verify that the client package has permission
495e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * to access browse media information before returning the root id; it
496e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * should return null if the client is not allowed to access this
497e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * information.
498e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * </p>
499e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
500e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientPackageName The package name of the application which is
501e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            requesting access to browse media.
502e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientUid The uid of the application which is requesting access to
503e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            browse media.
504e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param rootHints An optional bundle of service-specific arguments to send
505e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            to the media browse service when connecting and retrieving the
506e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            root id for browsing, or null if none. The contents of this
507e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            bundle may affect the information returned when browsing.
508e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @return The {@link BrowserRoot} for accessing this app's content or null.
509e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
510e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
511e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            int clientUid, @Nullable Bundle rootHints);
512e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
513e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
514e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about the children of a media item.
515e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
516e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}
517e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * with the list of children. If loading the children will be an expensive
518e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * operation that should be performed on another thread,
519e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link Result#detach result.detach} may be called before returning from
520e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * this function, and then {@link Result#sendResult result.sendResult}
521e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * called when the loading is complete.
522e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
523e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose children are to be
524e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            queried.
525e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the list of children to, or null if the
526e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            id is invalid.
527e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
528e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract void onLoadChildren(@NonNull String parentId,
529096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result);
530e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
531e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
532e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about a specific media item.
533e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
534e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}. If
535e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * loading the item will be an expensive operation {@link Result#detach
536e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * result.detach} may be called before returning from this function, and
537e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * then {@link Result#sendResult result.sendResult} called when the item has
538e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * been loaded.
539e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
540e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The default implementation sends a null result.
541e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
542e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param itemId The id for the specific
543096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     *            {@link MediaBrowserCompat.MediaItem}.
544e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the item to, or null if the id is
545e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            invalid.
546e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
547096f2531cb790bc1106377d2da344614a3b88d39Jae Seo    public void onLoadItem(String itemId, Result<MediaBrowserCompat.MediaItem> result) {
548e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        result.sendResult(null);
549e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
550e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
551e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
552e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call to set the media session.
553e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
554e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This should be called as soon as possible during the service's startup.
555e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * It may only be called once.
556e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
557096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @param token The token for the service's {@link MediaSessionCompat}.
558e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
559e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void setSessionToken(final MediaSessionCompat.Token token) {
560e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (token == null) {
561e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("Session token may not be null.");
562e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
563e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (mSession != null) {
564e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("The session token has already been set.");
565e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
566e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mSession = token;
567e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mHandler.post(new Runnable() {
568e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
569e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            public void run() {
570e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                for (IBinder key : mConnections.keySet()) {
571e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(key);
572e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    try {
573e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        connection.callbacks.onConnect(connection.root.getRootId(), token,
574e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                connection.root.getExtras());
575e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    } catch (RemoteException e) {
576e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
577e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        mConnections.remove(key);
578e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
579e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
580e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
581e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        });
582e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
583e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
584e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
585e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Gets the session token, or null if it has not yet been created
586e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * or if it has been destroyed.
587e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
588e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public @Nullable MediaSessionCompat.Token getSessionToken() {
589e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return mSession;
590e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
591e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
592e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
593e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Notifies all connected media browsers that the children of
594e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * the specified parent id have changed in some way.
595e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This will cause browsers to fetch subscribed content again.
596e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
597e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose
598e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * children changed.
599e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
600e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void notifyChildrenChanged(@NonNull final String parentId) {
601e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (parentId == null) {
602e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
603e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
604e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mHandler.post(new Runnable() {
605e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
606e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            public void run() {
607e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                for (IBinder binder : mConnections.keySet()) {
608e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(binder);
609e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (connection.subscriptions.contains(parentId)) {
610e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        performLoadChildren(parentId, connection);
611e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
612e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
613e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
614e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        });
615e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
616e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
617e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
618e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Return whether the given package is one of the ones that is owned by the uid.
619e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
620e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private boolean isValidPackage(String pkg, int uid) {
621e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (pkg == null) {
622e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return false;
623e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
624e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final PackageManager pm = getPackageManager();
625e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final String[] packages = pm.getPackagesForUid(uid);
626e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final int N = packages.length;
627e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        for (int i=0; i<N; i++) {
628e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (packages[i].equals(pkg)) {
629e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return true;
630e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
631e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
632e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return false;
633e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
634e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
635e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
636e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Save the subscription and if it is a new subscription send the results.
637e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
638e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private void addSubscription(String id, ConnectionRecord connection) {
639e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // Save the subscription
640e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        connection.subscriptions.add(id);
641e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
642e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // send the results
643e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        performLoadChildren(id, connection);
644e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
645e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
646e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
647e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call onLoadChildren and then send the results back to the connection.
648e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
649e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Callers must make sure that this connection is still connected.
650e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
651e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private void performLoadChildren(final String parentId, final ConnectionRecord connection) {
652096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<List<MediaBrowserCompat.MediaItem>> result
653096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
654e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
655096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            void onResultSent(List<MediaBrowserCompat.MediaItem> list) {
656e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                if (list == null) {
657e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    throw new IllegalStateException("onLoadChildren sent null list for id "
658e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            + parentId);
659e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
660e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
661e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (DBG) {
662e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
663e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
664e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
665e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    return;
666e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
667e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
668e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                try {
669e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    connection.callbacks.onLoadChildren(parentId, list);
670e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                } catch (RemoteException ex) {
671e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    // The other side is in the process of crashing.
672e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
673e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            + " package=" + connection.pkg);
674e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
675e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
676e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        };
677e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
678e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        onLoadChildren(parentId, result);
679e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
680e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
681e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
682e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for package=" + connection.pkg + " id=" + parentId);
683e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
684e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
685e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
686e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private void performLoadItem(String itemId, final ResultReceiver receiver) {
687096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<MediaBrowserCompat.MediaItem> result =
688096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                new Result<MediaBrowserCompat.MediaItem>(itemId) {
689e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
690096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            void onResultSent(MediaBrowserCompat.MediaItem item) {
691e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                Bundle bundle = new Bundle();
692e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                bundle.putParcelable(KEY_MEDIA_ITEM, item);
693e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                receiver.send(0, bundle);
694e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
695e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        };
696e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
697096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        MediaBrowserServiceCompat.this.onLoadItem(itemId, result);
698e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
699e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
700e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
701e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for id=" + itemId);
702e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
703e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
704e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
705e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
706e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Contains information that the browser service needs to send to the client
707e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * when first connected.
708e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
709e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final class BrowserRoot {
710e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private String mRootId;
711e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private Bundle mExtras;
712e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
713e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
714e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Constructs a browser root.
715e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param rootId The root id for browsing.
716e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param extras Any extras about the browser service.
717e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
718e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
719e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (rootId == null) {
720e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
721e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        "Use null for BrowserRoot instead.");
722e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
723e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mRootId = rootId;
724e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mExtras = extras;
725e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
726e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
727e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
728e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Gets the root id for browsing.
729e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
730e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public String getRootId() {
731e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mRootId;
732e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
733e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
734e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
735e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Gets any extras about the brwoser service.
736e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
737e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public Bundle getExtras() {
738e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mExtras;
739e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
740e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
741e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo}
742