MediaBrowserServiceCompat.java revision 7a7ac26ee6d9ff4f5410991fa9cc97d0090ddaef
1e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/* 2e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Copyright (C) 2015 The Android Open Source Project 3e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 4e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Licensed under the Apache License, Version 2.0 (the "License"); 5e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * you may not use this file except in compliance with the License. 6e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * You may obtain a copy of the License at 7e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 8e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * http://www.apache.org/licenses/LICENSE-2.0 9e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 10e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Unless required by applicable law or agreed to in writing, software 11e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * distributed under the License is distributed on an "AS IS" BASIS, 12e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * See the License for the specific language governing permissions and 14e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * limitations under the License. 15e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 16e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 17e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seopackage android.support.v4.media; 18e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 19e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.app.Service; 20e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.Intent; 21e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.content.pm.PackageManager; 22e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Binder; 239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Build; 24e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Bundle; 25e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.Handler; 26e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.IBinder; 273f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Message; 283f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Limimport android.os.Messenger; 299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport android.os.Parcel; 30e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.os.RemoteException; 3182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport android.support.annotation.IntDef; 32e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.NonNull; 33e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.annotation.Nullable; 3482cf659fd8dcc28e182274b17a401023ab879deaSungsoo Limimport android.support.v4.app.BundleCompat; 35e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.media.session.MediaSessionCompat; 36e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.os.ResultReceiver; 37e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.support.v4.util.ArrayMap; 387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Limimport android.support.v4.util.Pair; 39e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.text.TextUtils; 40e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport android.util.Log; 41e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 42e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.FileDescriptor; 43e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.io.PrintWriter; 4482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.lang.annotation.Retention; 4582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.lang.annotation.RetentionPolicy; 469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Limimport java.util.ArrayList; 4782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.util.Collections; 4882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Limimport java.util.HashMap; 49e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seoimport java.util.List; 50e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 5182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Limimport static android.support.v4.media.MediaBrowserProtocol.*; 5282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim 53e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo/** 54e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Base class for media browse services. 55e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 56e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Media browse services enable applications to browse media content provided by an application 5782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * and ask the application to start playing it. They may also be used to control content that 58096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * is already playing by way of a {@link MediaSessionCompat}. 59e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p> 60e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 61e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * To extend this class, you must declare the service in your manifest file with 62e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * an intent filter with the {@link #SERVICE_INTERFACE} action. 63e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 64e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * For example: 65e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p><pre> 66096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * <service android:name=".MyMediaBrowserServiceCompat" 67e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * android:label="@string/service_name" > 68e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <intent-filter> 699703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim * <action android:name="android.media.browse.MediaBrowserService" /> 70e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </intent-filter> 71e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </service> 72e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </pre> 73e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 74096f2531cb790bc1106377d2da344614a3b88d39Jae Seopublic abstract class MediaBrowserServiceCompat extends Service { 757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim private static final String TAG = "MBServiceCompat"; 767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 77e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim private MediaBrowserServiceImpl mImpl; 799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 80e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 81e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * The {@link Intent} that must be declared as handled by the service. 82e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService"; 84e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 85e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 86e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * A key for passing the MediaItem to the ResultReceiver in getItem. 87e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 88e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @hide 89e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 90e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public static final String KEY_MEDIA_ITEM = "media_item"; 91e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 9282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001; 9382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 9482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim /** @hide */ 9582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim @Retention(RetentionPolicy.SOURCE) 9682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED }) 9782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim private @interface ResultFlags { } 9882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 9982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>(); 1003f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim private final ServiceHandler mHandler = new ServiceHandler(); 101e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo MediaSessionCompat.Token mSession; 102e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 1039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim interface MediaBrowserServiceImpl { 1043f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim void onCreate(); 1059703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim IBinder onBind(Intent intent); 1067a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim void setSessionToken(MediaSessionCompat.Token token); 1079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 1089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 1099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim class MediaBrowserServiceImplBase implements MediaBrowserServiceImpl { 1103f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim private Messenger mMessenger; 1119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 1129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim @Override 1139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void onCreate() { 1143f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim mMessenger = new Messenger(mHandler); 1159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 1169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 1179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim @Override 1189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public IBinder onBind(Intent intent) { 1199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim if (SERVICE_INTERFACE.equals(intent.getAction())) { 1203f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim return mMessenger.getBinder(); 1219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 1229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim return null; 1239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 1247a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim 1257a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim @Override 1267a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void setSessionToken(final MediaSessionCompat.Token token) { 1277a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mHandler.post(new Runnable() { 1287a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim @Override 1297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void run() { 1307a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim for (IBinder key : mConnections.keySet()) { 1317a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim ConnectionRecord connection = mConnections.get(key); 1327a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim try { 1337a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim connection.callbacks.onConnect(connection.root.getRootId(), token, 1347a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim connection.root.getExtras()); 1357a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } catch (RemoteException e) { 1367a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid."); 1377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mConnections.remove(key); 1387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 1397a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 1407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 1417a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim }); 1427a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 1439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 1449703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 1457a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl, 1467a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim MediaBrowserServiceCompatApi21.ServiceCompatProxy { 1479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim private Object mServiceObj; 1487a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim private Messenger mMessenger; 1499703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 1509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim @Override 1519703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void onCreate() { 1527a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceObj = MediaBrowserServiceCompatApi21.createService( 1537a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim MediaBrowserServiceCompat.this, this); 1547a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim MediaBrowserServiceCompatApi21.onCreate(mServiceObj); 1559703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 1569703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 1579703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim @Override 1589703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public IBinder onBind(Intent intent) { 1599703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim return MediaBrowserServiceCompatApi21.onBind(mServiceObj, intent); 1609703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 16182cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim 16282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim @Override 1637a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void setSessionToken(MediaSessionCompat.Token token) { 1647a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim MediaBrowserServiceCompatApi21.setSessionToken(mServiceObj, token.getToken()); 16582cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim } 16682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim 16782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim @Override 1687a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public MediaBrowserServiceCompatApi21.BrowserRoot onGetRoot( 1697a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim String clientPackageName, int clientUid, Bundle rootHints) { 1707a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim Bundle rootExtras = null; 1717a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (rootHints != null && rootHints.getInt(EXTRA_CLIENT_VERSION, 0) != 0) { 1727a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim rootHints.remove(EXTRA_CLIENT_VERSION); 1737a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mMessenger = new Messenger(mHandler); 1747a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim rootExtras = new Bundle(); 1757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT); 1767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder()); 1777a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 1787a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot( 1797a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim clientPackageName, clientUid, rootHints); 1807a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (root == null) { 1817a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim return null; 1827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 1837a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (rootExtras == null) { 1847a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim rootExtras = root.getExtras(); 1857a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } else if (root.getExtras() != null) { 1867a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim rootExtras.putAll(root.getExtras()); 1877a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 1887a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim return new MediaBrowserServiceCompatApi21.BrowserRoot( 1897a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim root.getRootId(), rootExtras); 19082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim } 19101b922eddadab50910ce289921001a63855e1f8eSungsoo Lim 19201b922eddadab50910ce289921001a63855e1f8eSungsoo Lim @Override 1937a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void onLoadChildren(String parentId, 1947a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim final MediaBrowserServiceCompatApi21.ResultWrapper resultWrapper) { 19501b922eddadab50910ce289921001a63855e1f8eSungsoo Lim 1967a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim final Result<List<MediaBrowserCompat.MediaItem>> result 1977a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) { 1987a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim @Override 1997a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flag) { 2007a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim List<Parcel> parcelList = null; 2017a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (list != null) { 2027a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim parcelList = new ArrayList<>(); 2037a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim for (MediaBrowserCompat.MediaItem item : list) { 2047a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim Parcel parcel = Parcel.obtain(); 2057a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim item.writeToParcel(parcel, 0); 2067a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim parcelList.add(parcel); 2077a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 2087a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 2097a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim resultWrapper.sendResult(parcelList); 2107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 2117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim 2127a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim @Override 2137a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void detach() { 2147a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim resultWrapper.detach(); 2157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 2167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim }; 2177a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim MediaBrowserServiceCompat.this.onLoadChildren(parentId, result); 21801b922eddadab50910ce289921001a63855e1f8eSungsoo Lim } 21901b922eddadab50910ce289921001a63855e1f8eSungsoo Lim } 22001b922eddadab50910ce289921001a63855e1f8eSungsoo Lim 2213f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim private final class ServiceHandler extends Handler { 2227a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim private final ServiceBinderImpl mServiceBinderImpl = new ServiceBinderImpl(); 2233f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim 2243f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim @Override 2253f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim public void handleMessage(Message msg) { 226094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim Bundle data = msg.getData(); 2273f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim switch (msg.what) { 22882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim case CLIENT_MSG_CONNECT: 2297a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceBinderImpl.connect(data.getString(DATA_PACKAGE_NAME), 230094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim data.getInt(DATA_CALLING_UID), data.getBundle(DATA_ROOT_HINTS), 2313f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim new ServiceCallbacksCompat(msg.replyTo)); 2323f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim break; 23382cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim case CLIENT_MSG_DISCONNECT: 2347a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceBinderImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo)); 2353f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim break; 23682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim case CLIENT_MSG_ADD_SUBSCRIPTION: 2377a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceBinderImpl.addSubscription(data.getString(DATA_MEDIA_ITEM_ID), 2387a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN), 2397a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim data.getBundle(DATA_OPTIONS), 2407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim new ServiceCallbacksCompat(msg.replyTo)); 2413f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim break; 24282cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim case CLIENT_MSG_REMOVE_SUBSCRIPTION: 2437a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceBinderImpl.removeSubscription(data.getString(DATA_MEDIA_ITEM_ID), 2447a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim BundleCompat.getBinder(data, DATA_CALLBACK_TOKEN), 2457a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim new ServiceCallbacksCompat(msg.replyTo)); 2463f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim break; 24782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim case CLIENT_MSG_GET_MEDIA_ITEM: 2487a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceBinderImpl.getMediaItem(data.getString(DATA_MEDIA_ITEM_ID), 249094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim (ResultReceiver) data.getParcelable(DATA_RESULT_RECEIVER)); 2503f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim break; 25116d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim case CLIENT_MSG_REGISTER_CALLBACK_MESSENGER: 2527a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceBinderImpl.registerCallbacks(new ServiceCallbacksCompat(msg.replyTo)); 25316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim break; 25499f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim case CLIENT_MSG_UNREGISTER_CALLBACK_MESSENGER: 2557a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mServiceBinderImpl.unregisterCallbacks(new ServiceCallbacksCompat(msg.replyTo)); 25699f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim break; 2573f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim default: 25882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim Log.w(TAG, "Unhandled message: " + msg 25982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim + "\n Service version: " + SERVICE_VERSION_CURRENT 26082cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim + "\n Client version: " + msg.arg1); 2613f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } 2623f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } 2633f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim 264094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim @Override 265094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 266094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim // Binder.getCallingUid() in handleMessage will return the uid of this process. 267094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim // In order to get the right calling uid, Binder.getCallingUid() should be called here. 268094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim Bundle data = msg.getData(); 269d4c131eea07dc2593f70523979929e52cf80e31eSungsoo Lim data.setClassLoader(MediaBrowserCompat.class.getClassLoader()); 270094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim data.putInt(DATA_CALLING_UID, Binder.getCallingUid()); 271094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim return super.sendMessageAtTime(msg, uptimeMillis); 272094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim } 273094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim 2743f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim public void postOrRun(Runnable r) { 2753f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim if (Thread.currentThread() == getLooper().getThread()) { 2763f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim r.run(); 2773f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } else { 2783f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim post(r); 2793f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } 2803f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } 2813f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim 2827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public ServiceBinderImpl getServiceBinderImpl() { 2837a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim return mServiceBinderImpl; 2843f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } 2853f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } 2863f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim 287e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 288e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * All the info about a connection. 289e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 290e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo private class ConnectionRecord { 291e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo String pkg; 292e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo Bundle rootHints; 2939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim ServiceCallbacks callbacks; 294e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo BrowserRoot root; 2957a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap(); 296e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 297e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 298e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 299096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * Completion handler for asynchronous callback methods in {@link MediaBrowserServiceCompat}. 300e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 301e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Each of the methods that takes one of these to send the result must call 30282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * {@link #sendResult} to respond to the caller with the given results. If those 303e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * functions return without calling {@link #sendResult}, they must instead call 304e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * {@link #detach} before returning, and then may call {@link #sendResult} when 30582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * they are done. If more than one of those methods is called, an exception will 306e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * be thrown. 307e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 308096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * @see MediaBrowserServiceCompat#onLoadChildren 309096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * @see MediaBrowserServiceCompat#onLoadItem 310e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 3119703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public static class Result<T> { 312e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo private Object mDebug; 313e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo private boolean mDetachCalled; 314e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo private boolean mSendResultCalled; 31582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim private int mFlags; 316e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 317e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo Result(Object debug) { 318e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo mDebug = debug; 319e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 320e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 321e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 322e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Send the result back to the caller. 323e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 324e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void sendResult(T result) { 325e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (mSendResultCalled) { 326e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalStateException("sendResult() called twice for: " + mDebug); 327e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 328e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo mSendResultCalled = true; 32982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim onResultSent(result, mFlags); 330e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 331e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 332e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 333e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Detach this message from the current thread and allow the {@link #sendResult} 334e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * call to happen later. 335e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 336e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void detach() { 337e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (mDetachCalled) { 338e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalStateException("detach() called when detach() had already" 339e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " been called for: " + mDebug); 340e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 341e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (mSendResultCalled) { 342e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalStateException("detach() called when sendResult() had already" 343e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " been called for: " + mDebug); 344e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 345e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo mDetachCalled = true; 346e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 347e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 348e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo boolean isDone() { 349e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return mDetachCalled || mSendResultCalled; 350e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 351e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 35282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim void setFlags(@ResultFlags int flags) { 35382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim mFlags = flags; 35482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 35582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 356e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 357e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Called when the result is sent, after assertions about not being called twice 358e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * have happened. 359e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 36082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim void onResultSent(T result, @ResultFlags int flags) { 361e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 362e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 363e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 3647a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim private class ServiceBinderImpl { 365094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim public void connect(final String pkg, final int uid, final Bundle rootHints, 3669703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim final ServiceCallbacks callbacks) { 367e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 368e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (!isValidPackage(pkg, uid)) { 369e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid 370e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " package=" + pkg); 371e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 372e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 3733f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim mHandler.postOrRun(new Runnable() { 3749703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim @Override 3759703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void run() { 3769703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim final IBinder b = callbacks.asBinder(); 3779703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 37882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim // Clear out the old subscriptions. We are getting new ones. 3799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mConnections.remove(b); 3809703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 3819703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim final ConnectionRecord connection = new ConnectionRecord(); 3829703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim connection.pkg = pkg; 3839703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim connection.rootHints = rootHints; 3849703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim connection.callbacks = callbacks; 3859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 3869703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim connection.root = 3879703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim MediaBrowserServiceCompat.this.onGetRoot(pkg, uid, rootHints); 3889703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 3899703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim // If they didn't return something, don't allow this client. 3909703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim if (connection.root == null) { 3919703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim Log.i(TAG, "No root for client " + pkg + " from service " 3929703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim + getClass().getName()); 3939703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim try { 3949703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim callbacks.onConnectFailed(); 3959703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } catch (RemoteException ex) { 3969703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. " 3979703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim + "pkg=" + pkg); 3989703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 3999703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } else { 4009703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim try { 4019703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mConnections.put(b, connection); 4029703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim if (mSession != null) { 4039703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim callbacks.onConnect(connection.root.getRootId(), 4049703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mSession, connection.root.getExtras()); 405e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 4069703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } catch (RemoteException ex) { 4079703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim Log.w(TAG, "Calling onConnect() failed. Dropping client. " 4089703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim + "pkg=" + pkg); 4099703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mConnections.remove(b); 410e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 411e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 4129703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 4139703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim }); 414e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 415e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 4169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void disconnect(final ServiceCallbacks callbacks) { 4173f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim mHandler.postOrRun(new Runnable() { 4189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim @Override 4199703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void run() { 4209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim final IBinder b = callbacks.asBinder(); 4219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 42282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim // Clear out the old subscriptions. We are getting new ones. 4239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim final ConnectionRecord old = mConnections.remove(b); 4249703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim if (old != null) { 4259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim // TODO 426e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 4279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 4289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim }); 429e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 430e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 4317a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void addSubscription(final String id, final IBinder token, final Bundle options, 43282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim final ServiceCallbacks callbacks) { 4333f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim mHandler.postOrRun(new Runnable() { 4349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim @Override 4359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void run() { 4369703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim final IBinder b = callbacks.asBinder(); 4376a4150834d679d66abef50aff74d30ae2e846a32Sungsoo Lim 4389703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim // Get the record for the connection 4399703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim final ConnectionRecord connection = mConnections.get(b); 4409703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim if (connection == null) { 4419703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim Log.w(TAG, "addSubscription for callback that isn't registered id=" 4429703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim + id); 4439703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim return; 444493571364635be0190cea8ee230a601070391e6fIan Pedowitz } 4459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 4467a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim MediaBrowserServiceCompat.this.addSubscription(id, connection, token, options); 4479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 4489703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim }); 449e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 450e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 4517a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void removeSubscription(final String id, final IBinder token, 45282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim final ServiceCallbacks callbacks) { 4533f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim mHandler.postOrRun(new Runnable() { 454e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo @Override 455e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void run() { 456e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo final IBinder b = callbacks.asBinder(); 457e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 458e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo ConnectionRecord connection = mConnections.get(b); 459e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (connection == null) { 460e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo Log.w(TAG, "removeSubscription for callback that isn't registered id=" 461e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + id); 462e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return; 463e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 46482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim if (!MediaBrowserServiceCompat.this.removeSubscription( 4657a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim id, connection, token)) { 466e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo Log.w(TAG, "removeSubscription called for " + id 467e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " which is not subscribed"); 468e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 469e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 470e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo }); 471e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 472e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 473e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void getMediaItem(final String mediaId, final ResultReceiver receiver) { 474e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (TextUtils.isEmpty(mediaId) || receiver == null) { 475e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return; 476e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 477e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 4783f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim mHandler.postOrRun(new Runnable() { 479e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo @Override 480e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void run() { 481e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo performLoadItem(mediaId, receiver); 482e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 483e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo }); 484e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 48516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim 48616d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used. 48716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim public void registerCallbacks(final ServiceCallbacks callbacks) { 48816d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim mHandler.postOrRun(new Runnable() { 48916d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim @Override 49016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim public void run() { 49116d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim final IBinder b = callbacks.asBinder(); 49216d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim // Clear out the old subscriptions. We are getting new ones. 49316d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim mConnections.remove(b); 49416d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim 49516d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim final ConnectionRecord connection = new ConnectionRecord(); 49616d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim connection.callbacks = callbacks; 49716d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim mConnections.put(b, connection); 49816d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim } 49916d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim }); 50016d709bad185d0d5e9bf2ba88132fb592e63041dSungsoo Lim } 50199f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim 50299f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim // Used when {@link MediaBrowserProtocol#EXTRA_MESSENGER_BINDER} is used. 50399f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim public void unregisterCallbacks(final ServiceCallbacks callbacks) { 50499f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim mHandler.postOrRun(new Runnable() { 50599f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim @Override 50699f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim public void run() { 50799f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim final IBinder b = callbacks.asBinder(); 50899f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim mConnections.remove(b); 50999f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim } 51099f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim }); 51199f783676dda9a66b2f1a576c12ee2402d8bbcd0Sungsoo Lim } 512e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 513e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 5149703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim private interface ServiceCallbacks { 5159703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim IBinder asBinder(); 5169703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim void onConnect(String root, MediaSessionCompat.Token session, Bundle extras) 5179703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim throws RemoteException; 5189703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim void onConnectFailed() throws RemoteException; 51982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list, Bundle options) 5209703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim throws RemoteException; 5219703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5229703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 5239703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim private class ServiceCallbacksCompat implements ServiceCallbacks { 5243f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim final Messenger mCallbacks; 5259703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 5263f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim ServiceCallbacksCompat(Messenger callbacks) { 5279703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mCallbacks = callbacks; 5289703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5299703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 5309703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public IBinder asBinder() { 5313f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim return mCallbacks.getBinder(); 5329703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5339703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 5349703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras) 5359703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim throws RemoteException { 53682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim if (extras == null) { 53782cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim extras = new Bundle(); 53882cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim } 53982cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT); 5403f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim Bundle data = new Bundle(); 541094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim data.putString(DATA_MEDIA_ITEM_ID, root); 542094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim data.putParcelable(DATA_MEDIA_SESSION_TOKEN, session); 543094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim data.putBundle(DATA_ROOT_HINTS, extras); 544094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim sendRequest(SERVICE_MSG_ON_CONNECT, data); 5459703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5469703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 5479703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim public void onConnectFailed() throws RemoteException { 548094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null); 5499703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5509703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 55182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list, 55282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim Bundle options) throws RemoteException { 553094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim Bundle data = new Bundle(); 554094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim data.putString(DATA_MEDIA_ITEM_ID, mediaId); 55582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim data.putBundle(DATA_OPTIONS, options); 556ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim if (list != null) { 557094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim data.putParcelableArrayList(DATA_MEDIA_ITEM_LIST, 558ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list)); 559ae6d147640b8a868d2edff8ebac8d2a6bb03c594Sungsoo Lim } 560094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, data); 5613f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim } 5623f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim 563094c558a8a4f9be922846da811568d2970064e43Sungsoo Lim private void sendRequest(int what, Bundle data) throws RemoteException { 5643f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim Message msg = Message.obtain(); 5653f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim msg.what = what; 56682cf659fd8dcc28e182274b17a401023ab879deaSungsoo Lim msg.arg1 = SERVICE_VERSION_CURRENT; 5673f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim msg.setData(data); 5683f61f1657d13dacf4eb42d9371595b8075cff222Sungsoo Lim mCallbacks.send(msg); 5699703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5709703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5719703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim 572e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo @Override 573e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void onCreate() { 574e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo super.onCreate(); 5757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (Build.VERSION.SDK_INT >= 21) { 5769703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mImpl = new MediaBrowserServiceImplApi21(); 5779703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } else { 5789703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mImpl = new MediaBrowserServiceImplBase(); 5799703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim } 5809703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim mImpl.onCreate(); 581e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 582e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 583e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo @Override 584e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public IBinder onBind(Intent intent) { 5859703a1e215168b6b580430ec490ca616b6490c80Sungsoo Lim return mImpl.onBind(intent); 586e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 587e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 588e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo @Override 589e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 590e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 591e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 592e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 593e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Called to get the root information for browsing by a particular client. 594e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 595e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * The implementation should verify that the client package has permission 596e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * to access browse media information before returning the root id; it 597e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * should return null if the client is not allowed to access this 598e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * information. 599e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * </p> 600e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 601e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param clientPackageName The package name of the application which is 602e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * requesting access to browse media. 603e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param clientUid The uid of the application which is requesting access to 604e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * browse media. 605e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param rootHints An optional bundle of service-specific arguments to send 606e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * to the media browse service when connecting and retrieving the 607e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * root id for browsing, or null if none. The contents of this 608e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * bundle may affect the information returned when browsing. 609e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @return The {@link BrowserRoot} for accessing this app's content or null. 61026e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo * @see BrowserRoot#EXTRA_RECENT 61126e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo * @see BrowserRoot#EXTRA_OFFLINE 61226e88318c8f69f62f41591b0df0720cc48b4ce11Jae Seo * @see BrowserRoot#EXTRA_SUGGESTED 613e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 614e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName, 615e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo int clientUid, @Nullable Bundle rootHints); 616e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 617e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 618e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Called to get information about the children of a media item. 619e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 620e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Implementations must call {@link Result#sendResult result.sendResult} 621e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * with the list of children. If loading the children will be an expensive 622e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * operation that should be performed on another thread, 623e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * {@link Result#detach result.detach} may be called before returning from 624e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * this function, and then {@link Result#sendResult result.sendResult} 625e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * called when the loading is complete. 626e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 627e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param parentId The id of the parent media item whose children are to be 628e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * queried. 629e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param result The Result to send the list of children to, or null if the 630e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * id is invalid. 631e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 632e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public abstract void onLoadChildren(@NonNull String parentId, 633096f2531cb790bc1106377d2da344614a3b88d39Jae Seo @NonNull Result<List<MediaBrowserCompat.MediaItem>> result); 634e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 635e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 63682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * Called to get information about the children of a media item. 63782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * <p> 63882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * Implementations must call {@link Result#sendResult result.sendResult} 63982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * with the list of children. If loading the children will be an expensive 64082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * operation that should be performed on another thread, 64182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * {@link Result#detach result.detach} may be called before returning from 64282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * this function, and then {@link Result#sendResult result.sendResult} 64382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * called when the loading is complete. 64482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * 64582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * @param parentId The id of the parent media item whose children are to be 64682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * queried. 64782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * @param result The Result to send the list of children to, or null if the 64882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * id is invalid. 64982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * @param options A bundle of service-specific arguments sent from the media 65082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * browse. The information returned through the result should be 65182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * affected by the contents of this bundle. 65282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * {@hide} 65382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim */ 65482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim public void onLoadChildren(@NonNull String parentId, 65582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim @NonNull Result<List<MediaBrowserCompat.MediaItem>> result, @NonNull Bundle options) { 65682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim // To support backward compatibility, when the implementation of MediaBrowserService doesn't 65782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim // override onLoadChildren() with options, onLoadChildren() without options will be used 65882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim // instead, and the options will be applied in the implementation of result.onResultSent(). 65982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim result.setFlags(RESULT_FLAG_OPTION_NOT_HANDLED); 66082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim onLoadChildren(parentId, result); 66182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 66282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 66382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim /** 664e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Called to get information about a specific media item. 665e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 666e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Implementations must call {@link Result#sendResult result.sendResult}. If 667e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * loading the item will be an expensive operation {@link Result#detach 668e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * result.detach} may be called before returning from this function, and 669e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * then {@link Result#sendResult result.sendResult} called when the item has 670e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * been loaded. 671e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 672e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * The default implementation sends a null result. 673e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 674e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param itemId The id for the specific 675096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * {@link MediaBrowserCompat.MediaItem}. 676e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param result The Result to send the item to, or null if the id is 677e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * invalid. 678e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 679096f2531cb790bc1106377d2da344614a3b88d39Jae Seo public void onLoadItem(String itemId, Result<MediaBrowserCompat.MediaItem> result) { 680e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo result.sendResult(null); 681e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 682e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 683e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 684e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Call to set the media session. 685e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 686e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * This should be called as soon as possible during the service's startup. 687e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * It may only be called once. 688e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 689096f2531cb790bc1106377d2da344614a3b88d39Jae Seo * @param token The token for the service's {@link MediaSessionCompat}. 690e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 6917a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim public void setSessionToken(MediaSessionCompat.Token token) { 692e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (token == null) { 693e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalArgumentException("Session token may not be null."); 694e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 695e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (mSession != null) { 696e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalStateException("The session token has already been set."); 697e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 698e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo mSession = token; 6997a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim mImpl.setSessionToken(token); 700e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 701e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 702e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 703e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Gets the session token, or null if it has not yet been created 704e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * or if it has been destroyed. 705e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 706e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public @Nullable MediaSessionCompat.Token getSessionToken() { 707e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return mSession; 708e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 709e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 710e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 711e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Notifies all connected media browsers that the children of 712e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * the specified parent id have changed in some way. 713e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * This will cause browsers to fetch subscribed content again. 714e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * 715e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param parentId The id of the parent media item whose 716e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * children changed. 717e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 71882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim public void notifyChildrenChanged(@NonNull String parentId) { 71982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim notifyChildrenChangedInternal(parentId, null); 72082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 72182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 72282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim /** 72382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * Notifies all connected media browsers that the children of 72482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * the specified parent id have changed in some way. 72582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * This will cause browsers to fetch subscribed content again. 72682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * 72782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * @param parentId The id of the parent media item whose 72882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * children changed. 72982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * @param options A bundle of service-specific arguments to send 73082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * to the media browse. The contents of this bundle may 73182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * contain the information about the change. 73282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * {@hide} 73382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim */ 73482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) { 73582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim if (options == null) { 73682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged"); 73782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 73882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim notifyChildrenChangedInternal(parentId, options); 73982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 74082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 74182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim private void notifyChildrenChangedInternal(final String parentId, final Bundle options) { 742e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (parentId == null) { 743e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged"); 744e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 745e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo mHandler.post(new Runnable() { 746e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo @Override 747e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public void run() { 748e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo for (IBinder binder : mConnections.keySet()) { 749e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo ConnectionRecord connection = mConnections.get(binder); 7507a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim List<Pair<IBinder, Bundle>> callbackList = 7517a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim connection.subscriptions.get(parentId); 7527a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (callbackList != null) { 7537a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim for (Pair<IBinder, Bundle> callback : callbackList) { 7547a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (MediaBrowserCompatUtils.hasDuplicatedItems( 7557a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim options, callback.second)) { 7567a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim performLoadChildren(parentId, connection, callback.second); 75782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 75882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 759e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 760e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 761e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 762e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo }); 763e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 764e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 765e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 766e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Return whether the given package is one of the ones that is owned by the uid. 767e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 768e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo private boolean isValidPackage(String pkg, int uid) { 769e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (pkg == null) { 770e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return false; 771e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 772e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo final PackageManager pm = getPackageManager(); 773e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo final String[] packages = pm.getPackagesForUid(uid); 774e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo final int N = packages.length; 775e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo for (int i=0; i<N; i++) { 776e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (packages[i].equals(pkg)) { 777e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return true; 778e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 779e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 780e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return false; 781e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 782e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 783e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 784e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Save the subscription and if it is a new subscription send the results. 785e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 7867a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim private void addSubscription(String id, ConnectionRecord connection, IBinder token, 7877a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim Bundle options) { 788e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo // Save the subscription 7897a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id); 7907a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (callbackList == null) { 7917a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim callbackList = new ArrayList<>(); 79282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 7937a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim for (Pair<IBinder, Bundle> callback : callbackList) { 7947a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (token == callback.first 7957a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim && MediaBrowserCompatUtils.areSameOptions(options, callback.second)) { 79682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim return; 79782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 79882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 7997a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim callbackList.add(new Pair<>(token, options)); 8007a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim connection.subscriptions.put(id, callbackList); 801e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo // send the results 80282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim performLoadChildren(id, connection, options); 80382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 80482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 80582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim /** 80682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim * Remove the subscription. 80782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim */ 8087a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) { 8097a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (token == null) { 8107a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim return connection.subscriptions.remove(id) != null; 8117a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 81282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim boolean removed = false; 8137a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id); 8147a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (callbackList != null) { 8157a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim for (Pair<IBinder, Bundle> callback : callbackList) { 8167a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (token == callback.first) { 81782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim removed = true; 8187a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim callbackList.remove(callback); 81982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 82082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 8217a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (callbackList.size() == 0) { 82282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim connection.subscriptions.remove(id); 82382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 82482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 82582387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim return removed; 826e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 827e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 828e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 829e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Call onLoadChildren and then send the results back to the connection. 830e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * <p> 831e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Callers must make sure that this connection is still connected. 832e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 83382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim private void performLoadChildren(final String parentId, final ConnectionRecord connection, 83482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim final Bundle options) { 835096f2531cb790bc1106377d2da344614a3b88d39Jae Seo final Result<List<MediaBrowserCompat.MediaItem>> result 836096f2531cb790bc1106377d2da344614a3b88d39Jae Seo = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) { 837e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo @Override 83882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim void onResultSent(List<MediaBrowserCompat.MediaItem> list, @ResultFlags int flag) { 839e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (mConnections.get(connection.callbacks.asBinder()) != connection) { 8407a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (DEBUG) { 841e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo Log.d(TAG, "Not sending onLoadChildren result for connection that has" 842e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " been disconnected. pkg=" + connection.pkg + " id=" + parentId); 843e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 844e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return; 845e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 846e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 84782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim List<MediaBrowserCompat.MediaItem> filteredList = 84882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0 8497a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim ? applyOptions(list, options) : list; 850e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo try { 85182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim connection.callbacks.onLoadChildren(parentId, filteredList, options); 852e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } catch (RemoteException ex) { 853e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo // The other side is in the process of crashing. 854e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId 855e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " package=" + connection.pkg); 856e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 857e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 858e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo }; 859e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 86082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim if (options == null) { 86182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim onLoadChildren(parentId, result); 86282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } else { 86382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim onLoadChildren(parentId, result, options); 86482387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 865e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 866e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (!result.isDone()) { 867e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalStateException("onLoadChildren must call detach() or sendResult()" 868e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " before returning for package=" + connection.pkg + " id=" + parentId); 869e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 870e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 871e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 87282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim private List<MediaBrowserCompat.MediaItem> applyOptions(List<MediaBrowserCompat.MediaItem> list, 87382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim final Bundle options) { 8747a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (list == null) { 8757a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim return null; 8767a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim } 87782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim int page = options.getInt(MediaBrowserCompat.EXTRA_PAGE, -1); 87882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim int pageSize = options.getInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, -1); 87982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim if (page == -1 && pageSize == -1) { 88082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim return list; 88182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 8827a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim int fromIndex = pageSize * page; 88382387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim int toIndex = fromIndex + pageSize; 8847a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim if (page < 0 || pageSize < 1 || fromIndex >= list.size()) { 8857a7ac26ee6d9ff4f5410991fa9cc97d0090ddaefSungsoo Lim return Collections.EMPTY_LIST; 88682387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 88782387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim if (toIndex > list.size()) { 88882387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim toIndex = list.size(); 88982387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 89082387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim return list.subList(fromIndex, toIndex); 89182387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim } 89282387d9f1b1ed30028e4c9090ec5fadce7d146f1Sungsoo Lim 893e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo private void performLoadItem(String itemId, final ResultReceiver receiver) { 894096f2531cb790bc1106377d2da344614a3b88d39Jae Seo final Result<MediaBrowserCompat.MediaItem> result = 895096f2531cb790bc1106377d2da344614a3b88d39Jae Seo new Result<MediaBrowserCompat.MediaItem>(itemId) { 896a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim @Override 897a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim void onResultSent(MediaBrowserCompat.MediaItem item, @ResultFlags int flag) { 898a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim Bundle bundle = new Bundle(); 899a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim bundle.putParcelable(KEY_MEDIA_ITEM, item); 900a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim receiver.send(0, bundle); 901a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim } 902a7a03ae00cf642cb470e04c9a2164bad35627dbfSungsoo Lim }; 903e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 904096f2531cb790bc1106377d2da344614a3b88d39Jae Seo MediaBrowserServiceCompat.this.onLoadItem(itemId, result); 905e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 906e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (!result.isDone()) { 907e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalStateException("onLoadItem must call detach() or sendResult()" 908e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo + " before returning for id=" + itemId); 909e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 910e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 911e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 912e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 913e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Contains information that the browser service needs to send to the client 914e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * when first connected. 915e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 916e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public static final class BrowserRoot { 9178e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo /** 9188e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * The lookup key for a boolean that indicates whether the browser service should return a 9198e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * browser root for recently played media items. 9208e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9218e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * <p>When creating a media browser for a given media browser service, this key can be 9228e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * supplied as a root hint for retrieving media items that are recently played. 9238e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * If the media browser service can provide such media items, the implementation must return 9248e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. 9258e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9268e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * <p>The root hint may contain multiple keys. 9278e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9288e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * @see #EXTRA_OFFLINE 9298e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * @see #EXTRA_SUGGESTED 9308e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo */ 9318e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo public static final String EXTRA_RECENT = "android.service.media.extra.RECENT"; 9328e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo 9338e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo /** 9348e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * The lookup key for a boolean that indicates whether the browser service should return a 9358e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * browser root for offline media items. 9368e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9378e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * <p>When creating a media browser for a given media browser service, this key can be 9388e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * supplied as a root hint for retrieving media items that are can be played without an 9398e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * internet connection. 9408e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * If the media browser service can provide such media items, the implementation must return 9418e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. 9428e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9438e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * <p>The root hint may contain multiple keys. 9448e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9458e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * @see #EXTRA_RECENT 9468e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * @see #EXTRA_SUGGESTED 9478e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo */ 9488e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE"; 9498e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo 9508e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo /** 9518e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * The lookup key for a boolean that indicates whether the browser service should return a 9528e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * browser root for suggested media items. 9538e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9548e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * <p>When creating a media browser for a given media browser service, this key can be 9558e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * supplied as a root hint for retrieving the media items suggested by the media browser 9569763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo * service. The list of media items passed in {@link android.support.v4.media.MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded(String, List)} 9579763c3d83aeb160ad7c52f6e48aa4806477f1de0Jae Seo * is considered ordered by relevance, first being the top suggestion. 9588e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * If the media browser service can provide such media items, the implementation must return 9598e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back. 9608e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9618e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * <p>The root hint may contain multiple keys. 9628e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * 9638e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * @see #EXTRA_RECENT 9648e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo * @see #EXTRA_OFFLINE 9658e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo */ 9668e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED"; 9678e16eac9d287d2b663c0000169503e83206bd9a7Jae Seo 968e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo final private String mRootId; 969e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo final private Bundle mExtras; 970e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 971e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 972e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Constructs a browser root. 973e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param rootId The root id for browsing. 974e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * @param extras Any extras about the browser service. 975e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 976e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) { 977e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo if (rootId == null) { 978e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " + 979e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo "Use null for BrowserRoot instead."); 980e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 981e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo mRootId = rootId; 982e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo mExtras = extras; 983e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 984e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 985e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 986e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo * Gets the root id for browsing. 987e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 988e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public String getRootId() { 989e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return mRootId; 990e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 991e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo 992e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo /** 99323471681e408dfe4e44975e13e7575ab5a04bc8cJae Seo * Gets any extras about the browser service. 994e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo */ 995e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo public Bundle getExtras() { 996e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo return mExtras; 997e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 998e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo } 999e2dc54fb995a75eab424aafe4960799ed5512f4dJae Seo} 1000