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