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; 4284c462d3224ec640da9112362cb46ac502a39e7aSungsoo Limimport android.util.Pair; 4317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 4417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.io.FileDescriptor; 4517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.io.PrintWriter; 468e3fb586219ecfa82cff841b121543752a79f1faSungsoo Limimport java.lang.annotation.Retention; 478e3fb586219ecfa82cff841b121543752a79f1faSungsoo Limimport java.lang.annotation.RetentionPolicy; 4841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Limimport java.util.ArrayList; 4964f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Limimport java.util.Collections; 5041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Limimport java.util.HashMap; 5117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenimport java.util.List; 5217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 5317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen/** 5417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Base class for media browse services. 5517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 5617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Media browse services enable applications to browse media content provided by an application 578e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim * and ask the application to start playing it. They may also be used to control content that 5817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * is already playing by way of a {@link MediaSession}. 5917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 6017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 6117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * To extend this class, you must declare the service in your manifest file with 6292e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik * an intent filter with the {@link #SERVICE_INTERFACE} action. 6317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 6417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * For example: 6517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p><pre> 6617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <service android:name=".MyMediaBrowserService" 6717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * android:label="@string/service_name" > 6817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <intent-filter> 6917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <action android:name="android.media.browse.MediaBrowserService" /> 7017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </intent-filter> 7117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </service> 7217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </pre> 7317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 7417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 7517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chenpublic abstract class MediaBrowserService extends Service { 7617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private static final String TAG = "MediaBrowserService"; 7769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato private static final boolean DBG = false; 7869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 7969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato /** 8069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * The {@link Intent} that must be declared as handled by the service. 8169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato */ 8269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato @SdkConstant(SdkConstantType.SERVICE_ACTION) 8392e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; 8417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 85b1c88103fea75a19100d534e28366da0144d8138RoboErik /** 86b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown * A key for passing the MediaItem to the ResultReceiver in getItem. 87b1c88103fea75a19100d534e28366da0144d8138RoboErik * 88b1c88103fea75a19100d534e28366da0144d8138RoboErik * @hide 89b1c88103fea75a19100d534e28366da0144d8138RoboErik */ 90b1c88103fea75a19100d534e28366da0144d8138RoboErik public static final String KEY_MEDIA_ITEM = "media_item"; 91b1c88103fea75a19100d534e28366da0144d8138RoboErik 9241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001; 9341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim 948e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim /** @hide */ 958e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim @Retention(RetentionPolicy.SOURCE) 968e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED }) 978e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim private @interface ResultFlags { } 988e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim 9941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>(); 10051ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim private ConnectionRecord mCurConnection; 10117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private final Handler mHandler = new Handler(); 10217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private ServiceBinder mBinder; 10317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen MediaSession.Token mSession; 10417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 10517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 10617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * All the info about a connection. 10717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 10817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private class ConnectionRecord { 10917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen String pkg; 11017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Bundle rootHints; 11117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen IMediaBrowserServiceCallbacks callbacks; 112319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen BrowserRoot root; 11384c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>(); 11417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 11517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 11617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 11769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * Completion handler for asynchronous callback methods in {@link MediaBrowserService}. 11869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * <p> 11969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * Each of the methods that takes one of these to send the result must call 1208e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim * {@link #sendResult} to respond to the caller with the given results. If those 12169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * functions return without calling {@link #sendResult}, they must instead call 12269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * {@link #detach} before returning, and then may call {@link #sendResult} when 1238e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim * they are done. If more than one of those methods is called, an exception will 12469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * be thrown. 12569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * 12664f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * @see #onLoadChildren 12764f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * @see #onLoadItem 12817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 12969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato public class Result<T> { 13069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato private Object mDebug; 13169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato private boolean mDetachCalled; 13269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato private boolean mSendResultCalled; 1338e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim private int mFlags; 13469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 13569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato Result(Object debug) { 13669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato mDebug = debug; 13769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 13869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 13969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato /** 14069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * Send the result back to the caller. 14169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato */ 14269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato public void sendResult(T result) { 14369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato if (mSendResultCalled) { 14469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato throw new IllegalStateException("sendResult() called twice for: " + mDebug); 14569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 14669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato mSendResultCalled = true; 1478e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim onResultSent(result, mFlags); 14869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 14969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 15069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato /** 15169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * Detach this message from the current thread and allow the {@link #sendResult} 15269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * call to happen later. 15369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato */ 15469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato public void detach() { 15569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato if (mDetachCalled) { 15669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato throw new IllegalStateException("detach() called when detach() had already" 15769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato + " been called for: " + mDebug); 15869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 15969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato if (mSendResultCalled) { 16069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato throw new IllegalStateException("detach() called when sendResult() had already" 16169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato + " been called for: " + mDebug); 16269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 16369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato mDetachCalled = true; 16469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 16569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 16669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato boolean isDone() { 16769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato return mDetachCalled || mSendResultCalled; 16869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 16969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 1708e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim void setFlags(@ResultFlags int flags) { 1718e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim mFlags = flags; 17241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 17341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim 17469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato /** 17569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * Called when the result is sent, after assertions about not being called twice 17669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * have happened. 17769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato */ 1788e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim void onResultSent(T result, @ResultFlags int flags) { 17969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 18069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 18117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 18217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private class ServiceBinder extends IMediaBrowserService.Stub { 18317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 18417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void connect(final String pkg, final Bundle rootHints, 18517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final IMediaBrowserServiceCallbacks callbacks) { 18617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 18717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final int uid = Binder.getCallingUid(); 18817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (!isValidPackage(pkg, uid)) { 18917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid 19017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + " package=" + pkg); 19117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 19217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 19317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mHandler.post(new Runnable() { 19417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 19517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void run() { 19617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final IBinder b = callbacks.asBinder(); 19717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 1988e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim // Clear out the old subscriptions. We are getting new ones. 19917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mConnections.remove(b); 20017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 20117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final ConnectionRecord connection = new ConnectionRecord(); 20217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen connection.pkg = pkg; 20317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen connection.rootHints = rootHints; 20417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen connection.callbacks = callbacks; 20517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 20617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints); 20717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 20817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // If they didn't return something, don't allow this client. 20917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (connection.root == null) { 21017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.i(TAG, "No root for client " + pkg + " from service " 21117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + getClass().getName()); 21217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 21317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen callbacks.onConnectFailed(); 21417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (RemoteException ex) { 21517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. " 21617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + "pkg=" + pkg); 21717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 21817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } else { 21917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen try { 22017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mConnections.put(b, connection); 221d64c425f32174a66a3974c63211bf457005a8d6aRoboErik if (mSession != null) { 222d64c425f32174a66a3974c63211bf457005a8d6aRoboErik callbacks.onConnect(connection.root.getRootId(), 223d64c425f32174a66a3974c63211bf457005a8d6aRoboErik mSession, connection.root.getExtras()); 224d64c425f32174a66a3974c63211bf457005a8d6aRoboErik } 22517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } catch (RemoteException ex) { 22617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen Log.w(TAG, "Calling onConnect() failed. Dropping client. " 22717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen + "pkg=" + pkg); 22817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mConnections.remove(b); 22917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 23017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 23117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 23217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen }); 23317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 23417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 23517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 23617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void disconnect(final IMediaBrowserServiceCallbacks callbacks) { 23717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mHandler.post(new Runnable() { 23817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 23917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void run() { 24017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final IBinder b = callbacks.asBinder(); 24117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 2428e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim // Clear out the old subscriptions. We are getting new ones. 24317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final ConnectionRecord old = mConnections.remove(b); 24417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (old != null) { 24517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // TODO 24617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 24717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 24817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen }); 24917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 25017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 25141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim @Override 2526b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim public void addSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) { 2536b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim // do-nothing 2546b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim } 2556b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim 2566b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim @Override 25784c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim public void addSubscription(final String id, final IBinder token, final Bundle options, 25841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim final IMediaBrowserServiceCallbacks callbacks) { 25917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mHandler.post(new Runnable() { 26017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 26117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void run() { 26217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final IBinder b = callbacks.asBinder(); 26317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 26417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Get the record for the connection 26517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final ConnectionRecord connection = mConnections.get(b); 26617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (connection == null) { 267c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.w(TAG, "addSubscription for callback that isn't registered id=" 268c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik + id); 26917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return; 27017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 27117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 27284c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim MediaBrowserService.this.addSubscription(id, connection, token, options); 27317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 27417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen }); 27517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 27617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 27717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 2786b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim public void removeSubscriptionDeprecated(String id, IMediaBrowserServiceCallbacks callbacks) { 2796b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim // do-nothing 2806b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim } 2816b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim 2826b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim @Override 28384c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim public void removeSubscription(final String id, final IBinder token, 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 } 29684c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (!MediaBrowserService.this.removeSubscription(id, connection, token)) { 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 30551ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim public void getMediaItem(final String mediaId, final ResultReceiver receiver, 30651ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim final IMediaBrowserServiceCallbacks callbacks) { 307b1c88103fea75a19100d534e28366da0144d8138RoboErik if (TextUtils.isEmpty(mediaId) || receiver == null) { 308b1c88103fea75a19100d534e28366da0144d8138RoboErik return; 309b1c88103fea75a19100d534e28366da0144d8138RoboErik } 310b1c88103fea75a19100d534e28366da0144d8138RoboErik 311b1c88103fea75a19100d534e28366da0144d8138RoboErik mHandler.post(new Runnable() { 312b1c88103fea75a19100d534e28366da0144d8138RoboErik @Override 313b1c88103fea75a19100d534e28366da0144d8138RoboErik public void run() { 31451ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim final IBinder b = callbacks.asBinder(); 31551ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim ConnectionRecord connection = mConnections.get(b); 31651ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim if (connection == null) { 31751ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId); 31851ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim return; 31951ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim } 32051ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim performLoadItem(mediaId, connection, receiver); 321b1c88103fea75a19100d534e28366da0144d8138RoboErik } 322b1c88103fea75a19100d534e28366da0144d8138RoboErik }); 323b1c88103fea75a19100d534e28366da0144d8138RoboErik } 32417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 32517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 32617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 32717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void onCreate() { 32817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen super.onCreate(); 32917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen mBinder = new ServiceBinder(); 33017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 33117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 33217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 33317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public IBinder onBind(Intent intent) { 33492e565f71526141cb38f864dcb4eeb54d2cbf869RoboErik if (SERVICE_INTERFACE.equals(intent.getAction())) { 33517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return mBinder; 33617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 33717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return null; 33817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 33917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 34017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen @Override 34117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 34217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 34317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 34417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 345319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen * Called to get the root information for browsing by a particular client. 34617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 347b1c88103fea75a19100d534e28366da0144d8138RoboErik * The implementation should verify that the client package has permission 348b1c88103fea75a19100d534e28366da0144d8138RoboErik * to access browse media information before returning the root id; it 349b1c88103fea75a19100d534e28366da0144d8138RoboErik * should return null if the client is not allowed to access this 350b1c88103fea75a19100d534e28366da0144d8138RoboErik * information. 35117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * </p> 35217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 353b1c88103fea75a19100d534e28366da0144d8138RoboErik * @param clientPackageName The package name of the application which is 354b1c88103fea75a19100d534e28366da0144d8138RoboErik * requesting access to browse media. 355b1c88103fea75a19100d534e28366da0144d8138RoboErik * @param clientUid The uid of the application which is requesting access to 356b1c88103fea75a19100d534e28366da0144d8138RoboErik * browse media. 35717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * @param rootHints An optional bundle of service-specific arguments to send 358b1c88103fea75a19100d534e28366da0144d8138RoboErik * to the media browse service when connecting and retrieving the 359b1c88103fea75a19100d534e28366da0144d8138RoboErik * root id for browsing, or null if none. The contents of this 360b1c88103fea75a19100d534e28366da0144d8138RoboErik * bundle may affect the information returned when browsing. 361b1c88103fea75a19100d534e28366da0144d8138RoboErik * @return The {@link BrowserRoot} for accessing this app's content or null. 3620cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see BrowserRoot#EXTRA_RECENT 3630cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see BrowserRoot#EXTRA_OFFLINE 3640cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see BrowserRoot#EXTRA_SUGGESTED 36517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 3667e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName, 367319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen int clientUid, @Nullable Bundle rootHints); 36817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 36917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 37017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Called to get information about the children of a media item. 37169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato * <p> 372b1c88103fea75a19100d534e28366da0144d8138RoboErik * Implementations must call {@link Result#sendResult result.sendResult} 373b1c88103fea75a19100d534e28366da0144d8138RoboErik * with the list of children. If loading the children will be an expensive 374b1c88103fea75a19100d534e28366da0144d8138RoboErik * operation that should be performed on another thread, 375b1c88103fea75a19100d534e28366da0144d8138RoboErik * {@link Result#detach result.detach} may be called before returning from 376b1c88103fea75a19100d534e28366da0144d8138RoboErik * this function, and then {@link Result#sendResult result.sendResult} 377b1c88103fea75a19100d534e28366da0144d8138RoboErik * called when the loading is complete. 37864f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * </p><p> 37964f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * In case the media item does not have any children, call {@link Result#sendResult} 38097243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * with an empty list. When the given {@code parentId} is invalid, implementations must 38197243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke 38297243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * {@link MediaBrowser.SubscriptionCallback#onError}. 38364f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * </p> 38417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 385b1c88103fea75a19100d534e28366da0144d8138RoboErik * @param parentId The id of the parent media item whose children are to be 386b1c88103fea75a19100d534e28366da0144d8138RoboErik * queried. 38797243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * @param result The Result to send the list of children to. 38817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 389c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public abstract void onLoadChildren(@NonNull String parentId, 3903625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik @NonNull Result<List<MediaBrowser.MediaItem>> result); 39117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 39217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 39341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * Called to get information about the children of a media item. 39441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * <p> 39541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * Implementations must call {@link Result#sendResult result.sendResult} 39641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * with the list of children. If loading the children will be an expensive 39741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * operation that should be performed on another thread, 39841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * {@link Result#detach result.detach} may be called before returning from 39941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * this function, and then {@link Result#sendResult result.sendResult} 40041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * called when the loading is complete. 40164f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * </p><p> 40264f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * In case the media item does not have any children, call {@link Result#sendResult} 40397243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * with an empty list. When the given {@code parentId} is invalid, implementations must 40497243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke 40597243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * {@link MediaBrowser.SubscriptionCallback#onError}. 40664f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim * </p> 40741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * 40841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * @param parentId The id of the parent media item whose children are to be 40941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * queried. 41097243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * @param result The Result to send the list of children to. 41141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * @param options A bundle of service-specific arguments sent from the media 41241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * browse. The information returned through the result should be 41341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * affected by the contents of this bundle. 41441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim */ 41541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim public void onLoadChildren(@NonNull String parentId, 41641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim @NonNull Result<List<MediaBrowser.MediaItem>> result, @NonNull Bundle options) { 41741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim // To support backward compatibility, when the implementation of MediaBrowserService doesn't 41841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim // override onLoadChildren() with options, onLoadChildren() without options will be used 41941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim // instead, and the options will be applied in the implementation of result.onResultSent(). 4208e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED); 42141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim onLoadChildren(parentId, result); 42241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 42341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim 42441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim /** 425b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown * Called to get information about a specific media item. 426b1c88103fea75a19100d534e28366da0144d8138RoboErik * <p> 427b1c88103fea75a19100d534e28366da0144d8138RoboErik * Implementations must call {@link Result#sendResult result.sendResult}. If 428b1c88103fea75a19100d534e28366da0144d8138RoboErik * loading the item will be an expensive operation {@link Result#detach 429b1c88103fea75a19100d534e28366da0144d8138RoboErik * result.detach} may be called before returning from this function, and 430b1c88103fea75a19100d534e28366da0144d8138RoboErik * then {@link Result#sendResult result.sendResult} called when the item has 431b1c88103fea75a19100d534e28366da0144d8138RoboErik * been loaded. 43297243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * </p><p> 43397243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * When the given {@code itemId} is invalid, implementations must call 43497243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * {@link Result#sendResult result.sendResult} with {@code null}, which will 43597243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * invoke {@link MediaBrowser.ItemCallback#onError}. 43697243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * </p><p> 43797243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * The default implementation calls {@link Result#sendResult result.sendResult} 43897243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * with {@code null}. 43997243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * </p> 440b1c88103fea75a19100d534e28366da0144d8138RoboErik * 441b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown * @param itemId The id for the specific 442b1c88103fea75a19100d534e28366da0144d8138RoboErik * {@link android.media.browse.MediaBrowser.MediaItem}. 44397243a8ff2829e3bb80abacc77a6c7a01a660877Sungsoo Lim * @param result The Result to send the item to. 444b1c88103fea75a19100d534e28366da0144d8138RoboErik */ 445b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) { 446b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown result.sendResult(null); 447b1c88103fea75a19100d534e28366da0144d8138RoboErik } 448b1c88103fea75a19100d534e28366da0144d8138RoboErik 449b1c88103fea75a19100d534e28366da0144d8138RoboErik /** 45017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Call to set the media session. 45117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 452d64c425f32174a66a3974c63211bf457005a8d6aRoboErik * This should be called as soon as possible during the service's startup. 453d64c425f32174a66a3974c63211bf457005a8d6aRoboErik * It may only be called once. 454b1c88103fea75a19100d534e28366da0144d8138RoboErik * 455b1c88103fea75a19100d534e28366da0144d8138RoboErik * @param token The token for the service's {@link MediaSession}. 45617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 457d64c425f32174a66a3974c63211bf457005a8d6aRoboErik public void setSessionToken(final MediaSession.Token token) { 45817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (token == null) { 459d64c425f32174a66a3974c63211bf457005a8d6aRoboErik throw new IllegalArgumentException("Session token may not be null."); 46017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 461fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik if (mSession != null) { 462fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik throw new IllegalStateException("The session token has already been set."); 463fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik } 464fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik mSession = token; 465d64c425f32174a66a3974c63211bf457005a8d6aRoboErik mHandler.post(new Runnable() { 466d64c425f32174a66a3974c63211bf457005a8d6aRoboErik @Override 467d64c425f32174a66a3974c63211bf457005a8d6aRoboErik public void run() { 468d64c425f32174a66a3974c63211bf457005a8d6aRoboErik for (IBinder key : mConnections.keySet()) { 469d64c425f32174a66a3974c63211bf457005a8d6aRoboErik ConnectionRecord connection = mConnections.get(key); 470d64c425f32174a66a3974c63211bf457005a8d6aRoboErik try { 471fd228a383c0844d69da952460145b1aa3e00ffd7RoboErik connection.callbacks.onConnect(connection.root.getRootId(), token, 472d64c425f32174a66a3974c63211bf457005a8d6aRoboErik connection.root.getExtras()); 473d64c425f32174a66a3974c63211bf457005a8d6aRoboErik } catch (RemoteException e) { 474d64c425f32174a66a3974c63211bf457005a8d6aRoboErik Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid."); 475d64c425f32174a66a3974c63211bf457005a8d6aRoboErik mConnections.remove(key); 476d64c425f32174a66a3974c63211bf457005a8d6aRoboErik } 477d64c425f32174a66a3974c63211bf457005a8d6aRoboErik } 478d64c425f32174a66a3974c63211bf457005a8d6aRoboErik } 479d64c425f32174a66a3974c63211bf457005a8d6aRoboErik }); 48017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 48117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 48217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 48317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Gets the session token, or null if it has not yet been created 48417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * or if it has been destroyed. 48517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 48617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen public @Nullable MediaSession.Token getSessionToken() { 48717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return mSession; 48817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 48917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 49017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 49151ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim * Gets the root hints sent from the currently connected {@link MediaBrowser}. 492bd6c16908d922ecaf672d5f9a6d701fccb95b3ffJae Seo * The root hints are service-specific arguments included in an optional bundle sent to the 493bd6c16908d922ecaf672d5f9a6d701fccb95b3ffJae Seo * media browser service when connecting and retrieving the root id for browsing, or null if 494bd6c16908d922ecaf672d5f9a6d701fccb95b3ffJae Seo * none. The contents of this bundle may affect the information returned when browsing. 49551ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim * 49651ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren} 49751ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim * or {@link #onLoadItem} 498bd6c16908d922ecaf672d5f9a6d701fccb95b3ffJae Seo * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT 499bd6c16908d922ecaf672d5f9a6d701fccb95b3ffJae Seo * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE 500bd6c16908d922ecaf672d5f9a6d701fccb95b3ffJae Seo * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED 50151ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim */ 50251ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim public final Bundle getBrowserRootHints() { 50351ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim if (mCurConnection == null) { 50451ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim throw new IllegalStateException("This should be called inside of onLoadChildren or" 50551ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim + " onLoadItem methods"); 50651ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim } 50751ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints); 50851ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim } 50951ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim 51051ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim /** 51117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Notifies all connected media browsers that the children of 512c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * the specified parent id have changed in some way. 51317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * This will cause browsers to fetch subscribed content again. 51417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * 515c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * @param parentId The id of the parent media item whose 51617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * children changed. 51717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 51841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim public void notifyChildrenChanged(@NonNull String parentId) { 51941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim notifyChildrenChangedInternal(parentId, null); 52041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 52141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim 52241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim /** 52341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * Notifies all connected media browsers that the children of 52441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * the specified parent id have changed in some way. 52541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * This will cause browsers to fetch subscribed content again. 52641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * 52741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * @param parentId The id of the parent media item whose 52841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * children changed. 52941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * @param options A bundle of service-specific arguments to send 53041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * to the media browse. The contents of this bundle may 53141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * contain the information about the change. 53241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim */ 53341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) { 53441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim if (options == null) { 53541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged"); 53641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 53741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim notifyChildrenChangedInternal(parentId, options); 53841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 53941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim 54041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim private void notifyChildrenChangedInternal(final String parentId, final Bundle options) { 541c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik if (parentId == null) { 542c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged"); 543319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 5447e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen mHandler.post(new Runnable() { 5457e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen @Override 5467e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen public void run() { 5477e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen for (IBinder binder : mConnections.keySet()) { 5487e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen ConnectionRecord connection = mConnections.get(binder); 54984c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim List<Pair<IBinder, Bundle>> callbackList = 55084c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim connection.subscriptions.get(parentId); 55184c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (callbackList != null) { 55284c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim for (Pair<IBinder, Bundle> callback : callbackList) { 55384c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) { 55484c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim performLoadChildren(parentId, connection, callback.second); 55541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 55641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 5577e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen } 558319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 559319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 5607e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen }); 56117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 56217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 56317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 56417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Return whether the given package is one of the ones that is owned by the uid. 56517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 56617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen private boolean isValidPackage(String pkg, int uid) { 56717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (pkg == null) { 56817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return false; 56917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 57017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final PackageManager pm = getPackageManager(); 57117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final String[] packages = pm.getPackagesForUid(uid); 57217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen final int N = packages.length; 57317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen for (int i=0; i<N; i++) { 57417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen if (packages[i].equals(pkg)) { 57517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return true; 57617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 57717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 57817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen return false; 57917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 58017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 58117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 58217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Save the subscription and if it is a new subscription send the results. 58317d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 58484c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim private void addSubscription(String id, ConnectionRecord connection, IBinder token, 58584c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim Bundle options) { 58617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen // Save the subscription 58784c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id); 58884c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (callbackList == null) { 58984c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim callbackList = new ArrayList<>(); 59041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 59184c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim for (Pair<IBinder, Bundle> callback : callbackList) { 59284c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (token == callback.first 59384c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim && MediaBrowserUtils.areSameOptions(options, callback.second)) { 59441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim return; 59541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 59641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 59784c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim callbackList.add(new Pair<>(token, options)); 59884c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim connection.subscriptions.put(id, callbackList); 5997554d099e7f3a7cec166a999615245e7457bd620RoboErik // send the results 60041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim performLoadChildren(id, connection, options); 60141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 60241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim 60341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim /** 60441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim * Remove the subscription. 60541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim */ 60684c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) { 60784c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (token == null) { 60888a03d6e62edaac715081e091da7e9abc21d2c0dSungsoo Lim return connection.subscriptions.remove(id) != null; 60988a03d6e62edaac715081e091da7e9abc21d2c0dSungsoo Lim } 61041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim boolean removed = false; 61184c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id); 61284c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (callbackList != null) { 61384c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim for (Pair<IBinder, Bundle> callback : callbackList) { 61484c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (token == callback.first) { 61541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim removed = true; 61684c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim callbackList.remove(callback); 61741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 61841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 61984c462d3224ec640da9112362cb46ac502a39e7aSungsoo Lim if (callbackList.size() == 0) { 62041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim connection.subscriptions.remove(id); 62141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 62241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 62341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim return removed; 62417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 62517d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen 62617d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen /** 62717d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Call onLoadChildren and then send the results back to the connection. 62817d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * <p> 62917d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen * Callers must make sure that this connection is still connected. 63017d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen */ 63141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim private void performLoadChildren(final String parentId, final ConnectionRecord connection, 63241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim final Bundle options) { 6333625bf72cb8bcf3c7f8f8cd8d708d7206824cc62RoboErik final Result<List<MediaBrowser.MediaItem>> result 634c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik = new Result<List<MediaBrowser.MediaItem>>(parentId) { 63569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato @Override 6368e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) { 63769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato if (mConnections.get(connection.callbacks.asBinder()) != connection) { 63869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato if (DBG) { 63969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato Log.d(TAG, "Not sending onLoadChildren result for connection that has" 640c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik + " been disconnected. pkg=" + connection.pkg + " id=" + parentId); 64169b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 64269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato return; 64369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 64469b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 64541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim List<MediaBrowser.MediaItem> filteredList = 64641c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 64741c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim ? applyOptions(list, options) : list; 6482189f58ec5a4fa35b7e06d9d28abcef5d67f9c7fSungsoo Lim final ParceledListSlice<MediaBrowser.MediaItem> pls = 64941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim filteredList == null ? null : new ParceledListSlice<>(filteredList); 65069b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato try { 6516b7bda0bc36c1decf3272b5872f18f8044e6f1c6Sungsoo Lim connection.callbacks.onLoadChildrenWithOptions(parentId, pls, options); 65269b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } catch (RemoteException ex) { 65369b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato // The other side is in the process of crashing. 654c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId 65569b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato + " package=" + connection.pkg); 65669b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 65769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato } 65869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato }; 65969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 66051ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim mCurConnection = connection; 66141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim if (options == null) { 66241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim onLoadChildren(parentId, result); 66341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } else { 66441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim onLoadChildren(parentId, result, options); 66541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 66651ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim mCurConnection = null; 66769b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato 66869b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato if (!result.isDone()) { 66969b078599b8d8bc3e8f94d6cab881145f4e2c129Joe Onorato throw new IllegalStateException("onLoadChildren must call detach() or sendResult()" 670c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik + " before returning for package=" + connection.pkg + " id=" + parentId); 67117d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 67217d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen } 673319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 67441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim private List<MediaBrowser.MediaItem> applyOptions(List<MediaBrowser.MediaItem> list, 67541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim final Bundle options) { 67664f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim if (list == null) { 67764f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim return null; 67864f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim } 67941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim int page = options.getInt(MediaBrowser.EXTRA_PAGE, -1); 68041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim int pageSize = options.getInt(MediaBrowser.EXTRA_PAGE_SIZE, -1); 68141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim if (page == -1 && pageSize == -1) { 68241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim return list; 68341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 6840799bd07d780141208be7826491b221506e66160Sungsoo Lim int fromIndex = pageSize * page; 68541c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim int toIndex = fromIndex + pageSize; 6860799bd07d780141208be7826491b221506e66160Sungsoo Lim if (page < 0 || pageSize < 1 || fromIndex >= list.size()) { 68764f16f1dc1a6bdc1e16c7017a057da1620fac88bSungsoo Lim return Collections.EMPTY_LIST; 68841c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 68941c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim if (toIndex > list.size()) { 69041c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim toIndex = list.size(); 69141c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 69241c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim return list.subList(fromIndex, toIndex); 69341c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim } 69441c28a3eb18b01818197f331cb8c6596b17db735Sungsoo Lim 69551ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim private void performLoadItem(String itemId, final ConnectionRecord connection, 69651ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim final ResultReceiver receiver) { 697b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown final Result<MediaBrowser.MediaItem> result = 698b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown new Result<MediaBrowser.MediaItem>(itemId) { 699b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown @Override 7008e3fb586219ecfa82cff841b121543752a79f1faSungsoo Lim void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) { 701b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown Bundle bundle = new Bundle(); 702b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown bundle.putParcelable(KEY_MEDIA_ITEM, item); 703b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown receiver.send(0, bundle); 704b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown } 705b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown }; 706b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown 70751ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim mCurConnection = connection; 70851ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim onLoadItem(itemId, result); 70951ccdb0f399c82858c979d7de27e59345fc5692fSungsoo Lim mCurConnection = null; 710b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown 711b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown if (!result.isDone()) { 712b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown throw new IllegalStateException("onLoadItem must call detach() or sendResult()" 713b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown + " before returning for id=" + itemId); 714b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown } 715b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown } 716b7eff8828f0192bc4261a3142708c17c96bbe7b6Jeff Brown 7177e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen /** 7187e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen * Contains information that the browser service needs to send to the client 7197e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen * when first connected. 7207e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen */ 7217e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen public static final class BrowserRoot { 7220cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo /** 7230cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * The lookup key for a boolean that indicates whether the browser service should return a 7240cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * browser root for recently played media items. 7250cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7260cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * <p>When creating a media browser for a given media browser service, this key can be 7270cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * supplied as a root hint for retrieving media items that are recently played. 7280cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * If the media browser service can provide such media items, the implementation must return 7290cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. 7300cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7310cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * <p>The root hint may contain multiple keys. 7320cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7330cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see #EXTRA_OFFLINE 7340cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see #EXTRA_SUGGESTED 7350cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo */ 7360cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo public static final String EXTRA_RECENT = "android.service.media.extra.RECENT"; 7370cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo 7380cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo /** 7390cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * The lookup key for a boolean that indicates whether the browser service should return a 7400cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * browser root for offline media items. 7410cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7420cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * <p>When creating a media browser for a given media browser service, this key can be 7430cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * supplied as a root hint for retrieving media items that are can be played without an 7440cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * internet connection. 7450cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * If the media browser service can provide such media items, the implementation must return 7460cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. 7470cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7480cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * <p>The root hint may contain multiple keys. 7490cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7500cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see #EXTRA_RECENT 7510cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see #EXTRA_SUGGESTED 7520cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo */ 7530cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; 7540cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo 7550cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo /** 7560cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * The lookup key for a boolean that indicates whether the browser service should return a 7570cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * browser root for suggested media items. 7580cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7590cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * <p>When creating a media browser for a given media browser service, this key can be 7600cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * supplied as a root hint for retrieving the media items suggested by the media browser 7610cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * service. The list of media items passed in {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)} 7620cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * is considered ordered by relevance, first being the top suggestion. 7630cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * If the media browser service can provide such media items, the implementation must return 7640cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. 7650cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7660cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * <p>The root hint may contain multiple keys. 7670cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * 7680cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see #EXTRA_RECENT 7690cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo * @see #EXTRA_OFFLINE 7700cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo */ 7710cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; 7720cc5a9d8f66e84a181b54bfea2f257ee84f86de3Jae Seo 773c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik final private String mRootId; 774319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen final private Bundle mExtras; 7757e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen 7767e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen /** 7777e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen * Constructs a browser root. 778c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * @param rootId The root id for browsing. 7797e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen * @param extras Any extras about the browser service. 7807e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen */ 781c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) { 782c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik if (rootId == null) { 783c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " + 784319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen "Use null for BrowserRoot instead."); 785319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 786c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik mRootId = rootId; 787319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen mExtras = extras; 788319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 789319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 7907e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen /** 791c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik * Gets the root id for browsing. 7927e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen */ 793c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik public String getRootId() { 794c692d05951561e1d9fde952cc5389ec167bdb7bbRoboErik return mRootId; 795319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 796319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen 7977e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen /** 798082e873999e293397703fb511ff2edbab306f180Jae Seo * Gets any extras about the browser service. 7997e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen */ 8007e21fe8be9d374d31e75cdc2c9a11f977ba1faa0Yao Chen public Bundle getExtras() { 801319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen return mExtras; 802319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 803319f9a979c1c10c0c15ca50ee20e0a05e932cbb7Yao Chen } 80417d47989ee53c9e54f250d29a343ba949edf0ff9Yao Chen} 805