MediaBrowserServiceCompat.java revision d4c131eea07dc2593f70523979929e52cf80e31e
1e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/*
2e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Copyright (C) 2015 The Android Open Source Project
3e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
4e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Licensed under the Apache License, Version 2.0 (the "License");
5e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * you may not use this file except in compliance with the License.
6e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * You may obtain a copy of the License at
7e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
8e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *      http://www.apache.org/licenses/LICENSE-2.0
9e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
10e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Unless required by applicable law or agreed to in writing, software
11e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * distributed under the License is distributed on an "AS IS" BASIS,
12e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * See the License for the specific language governing permissions and
14e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * limitations under the License.
15e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */
16e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
17e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seopackage android.support.v4.media;
18e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
19e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.app.Service;
20e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.Intent;
21e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.pm.PackageManager;
22e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Binder;
239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Build;
24e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Bundle;
25e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Handler;
26e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.IBinder;
273f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Message;
283f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Messenger;
299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Parcel;
30e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.RemoteException;
31e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.NonNull;
32e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.Nullable;
3382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Limimport android.support.v4.app.BundleCompat;
34e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.media.session.MediaSessionCompat;
35e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.os.ResultReceiver;
36e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.util.ArrayMap;
37e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.text.TextUtils;
38e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.util.Log;
39e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
40e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.FileDescriptor;
41e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.PrintWriter;
429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport java.util.ArrayList;
43e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.util.HashSet;
44e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.util.List;
45e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
4682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Limimport static android.support.v4.media.MediaBrowserProtocol.*;
4782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
48e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/**
49e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Base class for media browse services.
50e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p>
51e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Media browse services enable applications to browse media content provided by an application
52e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * and ask the application to start playing it.  They may also be used to control content that
53096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * is already playing by way of a {@link MediaSessionCompat}.
54e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p>
55e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
56e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * To extend this class, you must declare the service in your manifest file with
57e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * an intent filter with the {@link #SERVICE_INTERFACE} action.
58e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *
59e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * For example:
60e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p><pre>
61096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * &lt;service android:name=".MyMediaBrowserServiceCompat"
62e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *          android:label="&#64;string/service_name" >
63e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;intent-filter>
649703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim *         &lt;action android:name="android.media.browse.MediaBrowserService" />
65e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo *     &lt;/intent-filter>
66e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * &lt;/service>
67e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </pre>
68e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */
69096f2531cb790bc1106377d2da344614a3b88d39Jae Seopublic abstract class MediaBrowserServiceCompat extends Service {
70096f2531cb790bc1106377d2da344614a3b88d39Jae Seo    private static final String TAG = "MediaBrowserServiceCompat";
71e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private static final boolean DBG = false;
72e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
739703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private MediaBrowserServiceImpl mImpl;
749703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
75e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
76e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The {@link Intent} that must be declared as handled by the service.
77e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
79e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
80e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
81e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * A key for passing the MediaItem to the ResultReceiver in getItem.
82e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
83e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @hide
84e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
85e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final String KEY_MEDIA_ITEM = "media_item";
86e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
87e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
883f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private final ServiceHandler mHandler = new ServiceHandler();
89e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    MediaSessionCompat.Token mSession;
90e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
919703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    interface MediaBrowserServiceImpl {
923f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        void onCreate();
939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder onBind(Intent intent);
949703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
959703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
969703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl {
973f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        private Messenger mMessenger;
989703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
999703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1009703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
1013f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mMessenger = new Messenger(mHandler);
1029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
1069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            if (SERVICE_INTERFACE.equals(intent.getAction())) {
1073f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                return mMessenger.getBinder();
1089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            }
1099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return null;
1109703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
1129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl {
1149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        private Object mServiceObj;
1159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onCreate() {
1189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi21.createService();
11982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            MediaBrowserServiceCompatApi21.onCreate(mServiceObj, new ServiceImplApi21());
1209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
1229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
1239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder onBind(Intent intent) {
1249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent);
1259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
1269703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
1279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
12882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim    class MediaBrowserServiceImplApi23 implements MediaBrowserServiceImpl {
12982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        private Object mServiceObj;
13082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
13182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
13282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        public void onCreate() {
13382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            mServiceObj = MediaBrowserServiceCompatApi23.createService();
13482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            MediaBrowserServiceCompatApi23.onCreate(mServiceObj, new ServiceImplApi23());
13582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
13682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
13782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        @Override
13882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        public IBinder onBind(Intent intent) {
13982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            return MediaBrowserServiceCompatApi23.onBind(mServiceObj, intent);
14082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        }
14182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim    }
14282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim
1433f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private final class ServiceHandler extends Handler {
1443f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        private final ServiceImpl mServiceImpl = new ServiceImpl();
1453f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
1463f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        @Override
1473f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void handleMessage(Message msg) {
148094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
1493f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            switch (msg.what) {
15082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_CONNECT:
151094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                    mServiceImpl.connect(data.getString(DATA_PACKAGE_NAME),
152094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                            data.getInt(DATA_CALLING_UID), data.getBundle(DATA_ROOT_HINTS),
1533f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
1543f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
15582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_DISCONNECT:
1563f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    mServiceImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo));
1573f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
15882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_ADD_SUBSCRIPTION:
159094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                    mServiceImpl.addSubscription(data.getString(DATA_MEDIA_ITEM_ID),
1603f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
1613f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
16282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_REMOVE_SUBSCRIPTION:
163094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                    mServiceImpl.removeSubscription(data.getString(DATA_MEDIA_ITEM_ID),
1643f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                            new ServiceCallbacksCompat(msg.replyTo));
1653f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
16682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                case CLIENT_MSG_GET_MEDIA_ITEM:
167094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                    mServiceImpl.getMediaItem(data.getString(DATA_MEDIA_ITEM_ID),
168094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                            (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER));
1693f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                    break;
1703f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                default:
17182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    Log.w(TAG, "Unhandled message: " + msg
17282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Service version: " + SERVICE_VERSION_CURRENT
17382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                            + "\n  Client version: " + msg.arg1);
1743f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
1753f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
1763f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
177094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        @Override
178094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
179094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // Binder.getCallingUid() in handleMessage will return the uid of this process.
180094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            // In order to get the right calling uid, Binder.getCallingUid() should be called here.
181094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = msg.getData();
182d4c131eea07dc2593f70523979929e52cf80e31eSungsoo Lim            data.setClassLoader(MediaBrowserCompat.class.getClassLoader());
183094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putInt(DATA_CALLING_UID, Binder.getCallingUid());
184094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            return super.sendMessageAtTime(msg, uptimeMillis);
185094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        }
186094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim
1873f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public void postOrRun(Runnable r) {
1883f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            if (Thread.currentThread() == getLooper().getThread()) {
1893f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                r.run();
1903f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            } else {
1913f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim                post(r);
1923f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            }
1933f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
1943f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
1953f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        public ServiceImpl getServiceImpl() {
1963f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            return mServiceImpl;
1973f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
1983f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    }
1993f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
200e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
201e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * All the info about a connection.
202e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
203e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private class ConnectionRecord {
204e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        String pkg;
205e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Bundle rootHints;
2069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        ServiceCallbacks callbacks;
207e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        BrowserRoot root;
208e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        HashSet<String> subscriptions = new HashSet();
209e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
210e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
211e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
212096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}.
213e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
214e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Each of the methods that takes one of these to send the result must call
215e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link #sendResult} to respond to the caller with the given results.  If those
216e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * functions return without calling {@link #sendResult}, they must instead call
217e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link #detach} before returning, and then may call {@link #sendResult} when
218e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * they are done.  If more than one of those methods is called, an exception will
219e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * be thrown.
220e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
221096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadChildren
222096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @see MediaBrowserServiceCompat#onLoadItem
223e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
2249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    public static class Result<T> {
225e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private Object mDebug;
226e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mDetachCalled;
227e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        private boolean mSendResultCalled;
228e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
229e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        Result(Object debug) {
230e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDebug = debug;
231e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
232e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
233e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
234e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Send the result back to the caller.
235e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
236e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void sendResult(T result) {
237e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
238e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("sendResult() called twice for: " + mDebug);
239e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
240e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mSendResultCalled = true;
241e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            onResultSent(result);
242e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
243e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
244e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
245e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Detach this message from the current thread and allow the {@link #sendResult}
246e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * call to happen later.
247e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
248e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void detach() {
249e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mDetachCalled) {
250e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when detach() had already"
251e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
252e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
253e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (mSendResultCalled) {
254e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalStateException("detach() called when sendResult() had already"
255e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " been called for: " + mDebug);
256e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
257e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mDetachCalled = true;
258e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
259e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
260e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        boolean isDone() {
261e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mDetachCalled || mSendResultCalled;
262e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
263e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
264e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
265e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Called when the result is sent, after assertions about not being called twice
266e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * have happened.
267e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
268e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        void onResultSent(T result) {
269e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
270e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
271e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
2723f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim    private class ServiceImpl {
273094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        public void connect(final String pkg, final int uid, final Bundle rootHints,
2749703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                final ServiceCallbacks callbacks) {
275e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
276e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (!isValidPackage(pkg, uid)) {
277e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
278e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        + " package=" + pkg);
279e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
280e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
2813f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
2829703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
2839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
2849703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
2859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
2869703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // Clear out the old subscriptions.  We are getting new ones.
2879703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    mConnections.remove(b);
2889703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
2899703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = new ConnectionRecord();
2909703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.pkg = pkg;
2919703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.rootHints = rootHints;
2929703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.callbacks = callbacks;
2939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
2949703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    connection.root =
2959703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints);
2969703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
2979703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // If they didn't return something, don't allow this client.
2989703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection.root == null) {
2999703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.i(TAG, "No root for client " + pkg + " from service "
3009703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + getClass().getName());
3019703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
3029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            callbacks.onConnectFailed();
3039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
3049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
3059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
3069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        }
3079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    } else {
3089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        try {
3099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.put(b, connection);
3109703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            if (mSession != null) {
3119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                callbacks.onConnect(connection.root.getRootId(),
3129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                        mSession, connection.root.getExtras());
313e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            }
3149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        } catch (RemoteException ex) {
3159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            Log.w(TAG, "Calling onConnect() failed. Dropping client. "
3169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                    + "pkg=" + pkg);
3179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                            mConnections.remove(b);
318e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        }
319e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
3209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
3219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
322e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
323e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
3249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void disconnect(final ServiceCallbacks callbacks) {
3253f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
3269703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
3279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
3289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
3299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
3309703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // Clear out the old subscriptions.  We are getting new ones.
3319703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord old = mConnections.remove(b);
3329703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (old != null) {
3339703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        // TODO
334e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
3359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
3369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
337e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
338e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
339e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
3409703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void addSubscription(final String id, final ServiceCallbacks callbacks) {
3413f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
3429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
3439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                public void run() {
3449703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final IBinder b = callbacks.asBinder();
3456a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim
3469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    // Get the record for the connection
3479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    final ConnectionRecord connection = mConnections.get(b);
3489703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    if (connection == null) {
3499703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        Log.w(TAG, "addSubscription for callback that isn't registered id="
3509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                                + id);
3519703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                        return;
352493571364635be0190cea8ee230a601070391e6fIan Pedowitz                    }
3539703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
3549703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    MediaBrowserServiceCompat.this.addSubscription(id, connection);
3559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
3569703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            });
357e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
358e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
3599703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void removeSubscription(final String id, final ServiceCallbacks callbacks) {
3603f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
361e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
362e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
363e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    final IBinder b = callbacks.asBinder();
364e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
365e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(b);
366e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (connection == null) {
367e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription for callback that isn't registered id="
368e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + id);
369e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        return;
370e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
371e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (!connection.subscriptions.remove(id)) {
372e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "removeSubscription called for " + id
373e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " which is not subscribed");
374e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
375e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
376e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
377e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
378e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
379e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
380e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (TextUtils.isEmpty(mediaId) || receiver == null) {
381e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return;
382e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
383e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
3843f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mHandler.postOrRun(new Runnable() {
385e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                @Override
386e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                public void run() {
387e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    performLoadItem(mediaId, receiver);
388e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
389e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            });
390e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
391e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
392e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
39382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim    private class ServiceImplApi21 implements MediaBrowserServiceCompatApi21.ServiceImplApi21 {
3943f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        final ServiceImpl mServiceImpl;
3959703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
39682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        ServiceImplApi21() {
39782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            mServiceImpl = mHandler.getServiceImpl();
3989703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
3999703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4009703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
4019703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void connect(final String pkg, final Bundle rootHints,
4029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
403094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            mServiceImpl.connect(pkg, Binder.getCallingUid(), rootHints,
404094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                    new ServiceCallbacksApi21(callbacks));
4059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
4089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void disconnect(final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
4093f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mServiceImpl.disconnect(new ServiceCallbacksApi21(callbacks));
4109703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
4149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void addSubscription(
4159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                final String id, final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
4163f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mServiceImpl.addSubscription(id, new ServiceCallbacksApi21(callbacks));
4179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
4209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void removeSubscription(final String id,
4219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
4223f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mServiceImpl.removeSubscription(id, new ServiceCallbacksApi21(callbacks));
4239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
42482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim    }
4259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
42682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim    private class ServiceImplApi23 extends ServiceImplApi21
42782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            implements MediaBrowserServiceCompatApi23.ServiceImplApi23 {
4289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        @Override
42982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        public void getMediaItem(final String mediaId,
43082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                final MediaBrowserServiceCompatApi23.ItemCallback cb) {
4319703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            ResultReceiver receiverCompat = new ResultReceiver(mHandler) {
4329703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                @Override
4339703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                protected void onReceiveResult(int resultCode, Bundle resultData) {
43482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    MediaBrowserCompat.MediaItem item = resultData.getParcelable(KEY_MEDIA_ITEM);
43582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    Parcel itemParcel = null;
43682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    if (item != null) {
43782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                        itemParcel = Parcel.obtain();
43882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                        item.writeToParcel(itemParcel, 0);
43982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    }
44082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                    cb.onItemLoaded(resultCode, resultData, itemParcel);
4419703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
4429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            };
4433f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mServiceImpl.getMediaItem(mediaId, receiverCompat);
4449703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
4469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private interface ServiceCallbacks {
4489703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        IBinder asBinder();
4499703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
4509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
4519703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onConnectFailed() throws RemoteException;
4529703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
4539703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException;
4549703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
4559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4569703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private class ServiceCallbacksCompat implements ServiceCallbacks {
4573f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        final Messenger mCallbacks;
4589703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4593f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        ServiceCallbacksCompat(Messenger callbacks) {
4609703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks = callbacks;
4619703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4629703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4639703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder asBinder() {
4643f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            return mCallbacks.getBinder();
4659703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4669703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4679703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
4689703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException {
46982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            if (extras == null) {
47082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                extras = new Bundle();
47182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            }
47282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
4733f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Bundle data = new Bundle();
474094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, root);
475094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putParcelable(DATA_MEDIA_SESSION_TOKEN, session);
476094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putBundle(DATA_ROOT_HINTS, extras);
477094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT, data);
4789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4809703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnectFailed() throws RemoteException {
481094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null);
4829703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
4839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
4849703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
4859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException {
486094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            Bundle data = new Bundle();
487094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            data.putString(DATA_MEDIA_ITEM_ID, mediaId);
488ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            if (list != null) {
489094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim                data.putParcelableArrayList(DATA_MEDIA_ITEM_LIST,
490ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim                        list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
491ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim            }
492094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim            sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, data);
4933f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim        }
4943f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim
495094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim        private void sendRequest(int what, Bundle data) throws RemoteException {
4963f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            Message msg = Message.obtain();
4973f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.what = what;
49882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            msg.arg1 = SERVICE_VERSION_CURRENT;
4993f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            msg.setData(data);
5003f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim            mCallbacks.send(msg);
5019703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
5039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    private class ServiceCallbacksApi21 implements ServiceCallbacks {
5059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        final MediaBrowserServiceCompatApi21.ServiceCallbacks mCallbacks;
50682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        Messenger mMessenger;
5079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        ServiceCallbacksApi21(MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
5099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks = callbacks;
5109703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public IBinder asBinder() {
5139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            return mCallbacks.asBinder();
5149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
5179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException {
51882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            if (extras == null) {
51982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim                extras = new Bundle();
52082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            }
52182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            mMessenger = new Messenger(mHandler);
52282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            BundleCompat.putBinder(extras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
52382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
5249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks.onConnect(root, session.getToken(), extras);
5259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5269703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onConnectFailed() throws RemoteException {
5289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks.onConnectFailed();
5299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5309703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
5319703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
5329703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                throws RemoteException {
5339703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            List<Parcel> parcelList = null;
5349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            if (list != null) {
5359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                parcelList = new ArrayList<>();
5369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                for (MediaBrowserCompat.MediaItem item : list) {
5379703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    Parcel parcel = Parcel.obtain();
5389703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    item.writeToParcel(parcel, 0);
5399703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                    parcelList.add(parcel);
5409703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim                }
5419703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            }
5429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mCallbacks.onLoadChildren(mediaId, parcelList);
5439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5449703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim    }
5459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim
546e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
547e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void onCreate() {
548e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        super.onCreate();
54982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        if (Build.VERSION.SDK_INT >= 23) {
55082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim            mImpl = new MediaBrowserServiceImplApi23();
55182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim        } else if (Build.VERSION.SDK_INT >= 21) {
5529703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplApi21();
5539703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        } else {
5549703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim            mImpl = new MediaBrowserServiceImplBase();
5559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        }
5569703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        mImpl.onCreate();
557e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
558e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
559e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
560e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public IBinder onBind(Intent intent) {
5619703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim        return mImpl.onBind(intent);
562e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
563e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
564e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    @Override
565e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
566e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
567e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
568e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
569e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get the root information for browsing by a particular client.
570e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
571e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The implementation should verify that the client package has permission
572e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * to access browse media information before returning the root id; it
573e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * should return null if the client is not allowed to access this
574e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * information.
575e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * </p>
576e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
577e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientPackageName The package name of the application which is
578e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            requesting access to browse media.
579e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param clientUid The uid of the application which is requesting access to
580e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            browse media.
581e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param rootHints An optional bundle of service-specific arguments to send
582e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            to the media browse service when connecting and retrieving the
583e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            root id for browsing, or null if none. The contents of this
584e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            bundle may affect the information returned when browsing.
585e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @return The {@link BrowserRoot} for accessing this app's content or null.
586e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
587e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
588e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            int clientUid, @Nullable Bundle rootHints);
589e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
590e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
591e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about the children of a media item.
592e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
593e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}
594e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * with the list of children. If loading the children will be an expensive
595e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * operation that should be performed on another thread,
596e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * {@link Result#detach result.detach} may be called before returning from
597e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * this function, and then {@link Result#sendResult result.sendResult}
598e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * called when the loading is complete.
599e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
600e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose children are to be
601e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            queried.
602e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the list of children to, or null if the
603e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            id is invalid.
604e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
605e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public abstract void onLoadChildren(@NonNull String parentId,
606096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            @NonNull Result<List<MediaBrowserCompat.MediaItem>> result);
607e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
608e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
609e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Called to get information about a specific media item.
610e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
611e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Implementations must call {@link Result#sendResult result.sendResult}. If
612e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * loading the item will be an expensive operation {@link Result#detach
613e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * result.detach} may be called before returning from this function, and
614e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * then {@link Result#sendResult result.sendResult} called when the item has
615e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * been loaded.
616e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
617e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * The default implementation sends a null result.
618e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
619e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param itemId The id for the specific
620096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     *            {@link MediaBrowserCompat.MediaItem}.
621e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param result The Result to send the item to, or null if the id is
622e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *            invalid.
623e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
624096f2531cb790bc1106377d2da344614a3b88d39Jae Seo    public void onLoadItem(String itemId, Result<MediaBrowserCompat.MediaItem> result) {
625e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        result.sendResult(null);
626e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
627e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
628e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
629e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call to set the media session.
630e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
631e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This should be called as soon as possible during the service's startup.
632e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * It may only be called once.
633e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
634096f2531cb790bc1106377d2da344614a3b88d39Jae Seo     * @param token The token for the service's {@link MediaSessionCompat}.
635e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
636e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void setSessionToken(final MediaSessionCompat.Token token) {
637e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (token == null) {
638e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("Session token may not be null.");
639e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
640e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (mSession != null) {
641e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("The session token has already been set.");
642e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
643e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mSession = token;
644e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mHandler.post(new Runnable() {
645e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
646e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            public void run() {
647e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                for (IBinder key : mConnections.keySet()) {
648e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(key);
649e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    try {
650e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        connection.callbacks.onConnect(connection.root.getRootId(), token,
651e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                connection.root.getExtras());
652e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    } catch (RemoteException e) {
653e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
654e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        mConnections.remove(key);
655e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
656e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
657e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
658e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        });
659e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
660e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
661e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
662e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Gets the session token, or null if it has not yet been created
663e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * or if it has been destroyed.
664e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
665e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public @Nullable MediaSessionCompat.Token getSessionToken() {
666e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return mSession;
667e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
668e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
669e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
670e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Notifies all connected media browsers that the children of
671e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * the specified parent id have changed in some way.
672e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * This will cause browsers to fetch subscribed content again.
673e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     *
674e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * @param parentId The id of the parent media item whose
675e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * children changed.
676e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
677e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public void notifyChildrenChanged(@NonNull final String parentId) {
678e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (parentId == null) {
679e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
680e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
681e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        mHandler.post(new Runnable() {
682e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
683e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            public void run() {
684e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                for (IBinder binder : mConnections.keySet()) {
685e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    ConnectionRecord connection = mConnections.get(binder);
686e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (connection.subscriptions.contains(parentId)) {
687e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        performLoadChildren(parentId, connection);
688e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
689e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
690e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
691e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        });
692e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
693e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
694e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
695e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Return whether the given package is one of the ones that is owned by the uid.
696e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
697e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private boolean isValidPackage(String pkg, int uid) {
698e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (pkg == null) {
699e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return false;
700e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
701e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final PackageManager pm = getPackageManager();
702e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final String[] packages = pm.getPackagesForUid(uid);
703e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final int N = packages.length;
704e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        for (int i=0; i<N; i++) {
705e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (packages[i].equals(pkg)) {
706e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                return true;
707e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
708e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
709e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        return false;
710e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
711e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
712e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
713e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Save the subscription and if it is a new subscription send the results.
714e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
715e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private void addSubscription(String id, ConnectionRecord connection) {
716e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // Save the subscription
717e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        connection.subscriptions.add(id);
718e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
719e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        // send the results
720e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        performLoadChildren(id, connection);
721e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
722e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
723e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
724e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Call onLoadChildren and then send the results back to the connection.
725e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * <p>
726e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Callers must make sure that this connection is still connected.
727e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
728e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private void performLoadChildren(final String parentId, final ConnectionRecord connection) {
729096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<List<MediaBrowserCompat.MediaItem>> result
730096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
731e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
732096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            void onResultSent(List<MediaBrowserCompat.MediaItem> list) {
733e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
734e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    if (DBG) {
735e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        Log.d(TAG, "Not sending onLoadChildren result for connection that has"
736e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                                + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
737e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    }
738e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    return;
739e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
740e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
741e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                try {
742e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    connection.callbacks.onLoadChildren(parentId, list);
743e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                } catch (RemoteException ex) {
744e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    // The other side is in the process of crashing.
745e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
746e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                            + " package=" + connection.pkg);
747e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                }
748e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
749e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        };
750e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
751e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        onLoadChildren(parentId, result);
752e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
753e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
754e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
755e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for package=" + connection.pkg + " id=" + parentId);
756e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
757e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
758e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
759e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    private void performLoadItem(String itemId, final ResultReceiver receiver) {
760096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        final Result<MediaBrowserCompat.MediaItem> result =
761096f2531cb790bc1106377d2da344614a3b88d39Jae Seo                new Result<MediaBrowserCompat.MediaItem>(itemId) {
762e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            @Override
763096f2531cb790bc1106377d2da344614a3b88d39Jae Seo            void onResultSent(MediaBrowserCompat.MediaItem item) {
764e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                Bundle bundle = new Bundle();
765e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                bundle.putParcelable(KEY_MEDIA_ITEM, item);
766e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                receiver.send(0, bundle);
767e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
768e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        };
769e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
770096f2531cb790bc1106377d2da344614a3b88d39Jae Seo        MediaBrowserServiceCompat.this.onLoadItem(itemId, result);
771e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
772e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        if (!result.isDone()) {
773e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
774e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                    + " before returning for id=" + itemId);
775e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
776e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
777e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
778e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    /**
779e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * Contains information that the browser service needs to send to the client
780e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     * when first connected.
781e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo     */
782e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    public static final class BrowserRoot {
783e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private String mRootId;
784e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        final private Bundle mExtras;
785e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
786e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
787e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Constructs a browser root.
788e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param rootId The root id for browsing.
789e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * @param extras Any extras about the browser service.
790e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
791e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
792e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            if (rootId == null) {
793e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
794e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo                        "Use null for BrowserRoot instead.");
795e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            }
796e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mRootId = rootId;
797e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            mExtras = extras;
798e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
799e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
800e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
801e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         * Gets the root id for browsing.
802e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
803e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public String getRootId() {
804e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mRootId;
805e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
806e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo
807e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        /**
80823471681e408dfe4e44975e13e7575ab5a04bc8cJae Seo         * Gets any extras about the browser service.
809e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo         */
810e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        public Bundle getExtras() {
811e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo            return mExtras;
812e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo        }
813e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo    }
814e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo}
815