Support API 23 methods in MBC and MBSC
- Use an extra messenger between MBSC (MediaBrowserServiceCompat)
and MBC (MediaBrowserCompat) when the platform SDK version is
21 or 22 in order to support the new method added in API 23.
- Use version numbers for better forward compatibility.
Bug: 22917960
Change-Id: Idf4247ee78ebfcf34434b92531618cc35151d038
diff --git a/v4/Android.mk b/v4/Android.mk
index d6fb925..87ea80e 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -201,7 +201,6 @@
LOCAL_SDK_VERSION := 21
LOCAL_SRC_FILES := $(call all-java-files-under, api21)
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api20
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
include $(BUILD_STATIC_JAVA_LIBRARY)
support_module_src_files += $(LOCAL_SRC_FILES)
@@ -240,6 +239,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, java) \
$(call all-Iaidl-files-under, java)
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api23
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
include $(BUILD_STATIC_JAVA_LIBRARY)
support_module_src_files += $(LOCAL_SRC_FILES)
diff --git a/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java
index c9ca69e..b285296 100644
--- a/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -144,14 +144,4 @@
mSubscriptionCallback.onError(parentId);
}
}
-
- static class MediaItem {
- public static Object getDescription(Object mediaItem) {
- return ((MediaBrowser.MediaItem) mediaItem).getDescription();
- }
-
- public static int getFlags(Object mediaItem) {
- return ((MediaBrowser.MediaItem) mediaItem).getFlags();
- }
- }
}
diff --git a/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java b/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
index b8363af..1baa14c 100644
--- a/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
+++ b/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
@@ -35,23 +35,22 @@
class MediaBrowserServiceCompatApi21 {
public static Object createService() {
- return new MediaBrowserServiceAdaptor();
+ return new MediaBrowserServiceAdaptorApi21();
}
- public static void onCreate(Object serviceObj, ServiceImpl serviceImpl) {
- ((MediaBrowserServiceAdaptor) serviceObj).onCreate(serviceImpl);
+ public static void onCreate(Object serviceObj, ServiceImplApi21 serviceImpl) {
+ ((MediaBrowserServiceAdaptorApi21) serviceObj).onCreate(serviceImpl);
}
public static IBinder onBind(Object serviceObj, Intent intent) {
- return ((MediaBrowserServiceAdaptor) serviceObj).onBind(intent);
+ return ((MediaBrowserServiceAdaptorApi21) serviceObj).onBind(intent);
}
- public interface ServiceImpl {
+ public interface ServiceImplApi21 {
void connect(final String pkg, final Bundle rootHints, final ServiceCallbacks callbacks);
void disconnect(final ServiceCallbacks callbacks);
void addSubscription(final String id, final ServiceCallbacks callbacks);
void removeSubscription(final String id, final ServiceCallbacks callbacks);
- void getMediaItem(final String mediaId, final ResultReceiver receiver);
}
public interface ServiceCallbacks {
@@ -95,11 +94,11 @@
}
}
- private static class MediaBrowserServiceAdaptor {
- ServiceBinderProxy mBinder;
+ static class MediaBrowserServiceAdaptorApi21 {
+ ServiceBinderProxyApi21 mBinder;
- public void onCreate(ServiceImpl serviceImpl) {
- mBinder = new ServiceBinderProxy(serviceImpl);
+ public void onCreate(ServiceImplApi21 serviceImpl) {
+ mBinder = new ServiceBinderProxyApi21(serviceImpl);
}
public IBinder onBind(Intent intent) {
@@ -109,10 +108,10 @@
return null;
}
- private static class ServiceBinderProxy extends IMediaBrowserService.Stub {
- private final ServiceImpl mServiceImpl;
+ static class ServiceBinderProxyApi21 extends IMediaBrowserService.Stub {
+ final ServiceImplApi21 mServiceImpl;
- ServiceBinderProxy(ServiceImpl serviceImpl) {
+ ServiceBinderProxyApi21(ServiceImplApi21 serviceImpl) {
mServiceImpl = serviceImpl;
}
@@ -142,7 +141,7 @@
@Override
public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
- mServiceImpl.getMediaItem(mediaId, receiver);
+ // No operation since this method is added in API 23.
}
}
}
diff --git a/v4/api23/android/support/v4/media/MediaBrowserCompatApi23.java b/v4/api23/android/support/v4/media/MediaBrowserCompatApi23.java
new file mode 100644
index 0000000..1e9df1a
--- /dev/null
+++ b/v4/api23/android/support/v4/media/MediaBrowserCompatApi23.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media;
+
+import android.media.browse.MediaBrowser;
+import android.os.Parcel;
+import android.support.annotation.NonNull;
+
+class MediaBrowserCompatApi23 {
+
+ public static Object createItemCallback(ItemCallback callback) {
+ return new ItemCallbackProxy<>(callback);
+ }
+
+ public static void getItem(Object browserObj, String mediaId, Object itemCallbackObj) {
+ ((MediaBrowser) browserObj).getItem(mediaId, ((MediaBrowser.ItemCallback) itemCallbackObj));
+ }
+
+ interface ItemCallback {
+ void onItemLoaded(Parcel itemParcel);
+ void onError(@NonNull String itemId);
+ }
+
+ static class ItemCallbackProxy<T extends ItemCallback> extends MediaBrowser.ItemCallback {
+ protected final T mItemCallback;
+
+ public ItemCallbackProxy(T callback) {
+ mItemCallback = callback;
+ }
+
+ @Override
+ public void onItemLoaded(MediaBrowser.MediaItem item) {
+ Parcel parcel = Parcel.obtain();
+ item.writeToParcel(parcel, 0);
+ mItemCallback.onItemLoaded(parcel);
+ }
+
+ @Override
+ public void onError(@NonNull String itemId) {
+ mItemCallback.onError(itemId);
+ }
+ }
+}
diff --git a/v4/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java b/v4/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
new file mode 100644
index 0000000..fcaea40
--- /dev/null
+++ b/v4/api23/android/support/v4/media/MediaBrowserServiceCompatApi23.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media;
+
+import android.media.browse.MediaBrowser;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.service.media.MediaBrowserService;
+import android.util.Log;
+
+class MediaBrowserServiceCompatApi23 extends MediaBrowserServiceCompatApi21 {
+ private static final String TAG = "MediaBrowserServiceCompatApi21";
+
+ public static Object createService() {
+ return new MediaBrowserServiceAdaptorApi23();
+ }
+
+ public static void onCreate(Object serviceObj, ServiceImplApi23 serviceImpl) {
+ ((MediaBrowserServiceAdaptorApi23) serviceObj).onCreate(serviceImpl);
+ }
+
+ public interface ServiceImplApi23 extends ServiceImplApi21 {
+ void getMediaItem(final String mediaId, final ItemCallback cb);
+ }
+
+ public interface ItemCallback {
+ void onItemLoaded(int resultCode, Bundle resultData, Parcel itemParcel);
+ }
+
+ static class MediaBrowserServiceAdaptorApi23 extends MediaBrowserServiceAdaptorApi21 {
+
+ public void onCreate(ServiceImplApi23 serviceImpl) {
+ mBinder = new ServiceBinderProxyApi23(serviceImpl);
+ }
+
+ private static class ServiceBinderProxyApi23 extends ServiceBinderProxyApi21 {
+ ServiceImplApi23 mServiceImpl;
+
+ ServiceBinderProxyApi23(ServiceImplApi23 serviceImpl) {
+ super(serviceImpl);
+ mServiceImpl = serviceImpl;
+ }
+
+ @Override
+ public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
+ final String KEY_MEDIA_ITEM;
+ try {
+ KEY_MEDIA_ITEM = (String) MediaBrowserService.class.getDeclaredField(
+ "KEY_MEDIA_ITEM").get(null);
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ Log.i(TAG, "Failed to get KEY_MEDIA_ITEM via reflection", e);
+ return;
+ }
+
+ mServiceImpl.getMediaItem(mediaId, new ItemCallback() {
+ @Override
+ public void onItemLoaded(int resultCode, Bundle resultData, Parcel itemParcel) {
+ if (itemParcel != null) {
+ itemParcel.setDataPosition(0);
+ MediaBrowser.MediaItem item =
+ MediaBrowser.MediaItem.CREATOR.createFromParcel(itemParcel);
+ resultData.putParcelable(KEY_MEDIA_ITEM, item);
+ itemParcel.recycle();
+ }
+ receiver.send(resultCode, resultData);
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/v4/java/android/support/v4/media/MediaBrowserCompat.java b/v4/java/android/support/v4/media/MediaBrowserCompat.java
index 4884cf2..d46dc65 100644
--- a/v4/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/v4/java/android/support/v4/media/MediaBrowserCompat.java
@@ -31,6 +31,7 @@
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.app.BundleCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.os.ResultReceiver;
import android.support.v4.util.ArrayMap;
@@ -43,6 +44,8 @@
import java.util.Collections;
import java.util.List;
+import static android.support.v4.media.MediaBrowserProtocol.*;
+
/**
* Browses media content offered by a {@link MediaBrowserServiceCompat}.
* <p>
@@ -52,13 +55,7 @@
* @hide
*/
public final class MediaBrowserCompat {
- public static final String DATA_RESULT_RECEIVER = "data_result_receiver";
-
- // TODO: Consider introducing version numbers for MediaBrowserCompat and
- // MediaBrowserServiceCompat.
- public static final int MSG_ON_CONNECT = 1;
- public static final int MSG_ON_CONNECT_FAILED = 2;
- public static final int MSG_ON_LOAD_CHILDREN = 3;
+ private static final String TAG = "MediaBrowserCompat";
private final MediaBrowserImpl mImpl;
@@ -75,8 +72,9 @@
*/
public MediaBrowserCompat(Context context, ComponentName serviceComponent,
ConnectionCallback callback, Bundle rootHints) {
- // TODO: Implement MediaBrowserImplApi23.
- if (android.os.Build.VERSION.SDK_INT >= 21) {
+ if (android.os.Build.VERSION.SDK_INT >= 23) {
+ mImpl = new MediaBrowserImplApi23(context, serviceComponent, callback, rootHints);
+ } else if (android.os.Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaBrowserImplApi21(context, serviceComponent, callback, rootHints);
} else {
mImpl = new MediaBrowserImplBase(context, serviceComponent, callback, rootHints);
@@ -442,6 +440,16 @@
* Callback for receiving the result of {@link #getItem}.
*/
public static abstract class ItemCallback {
+ final Object mItemCallbackObj;
+
+ public ItemCallback() {
+ if (android.os.Build.VERSION.SDK_INT >= 23) {
+ mItemCallbackObj = MediaBrowserCompatApi23.createItemCallback(new StubApi23());
+ } else {
+ mItemCallbackObj = null;
+ }
+ }
+
/**
* Called when the item has been returned by the browser service.
*
@@ -457,6 +465,21 @@
*/
public void onError(@NonNull String itemId) {
}
+
+ private class StubApi23 implements MediaBrowserCompatApi23.ItemCallback {
+ @Override
+ public void onItemLoaded(Parcel itemParcel) {
+ itemParcel.setDataPosition(0);
+ MediaItem item = MediaBrowserCompat.MediaItem.CREATOR.createFromParcel(itemParcel);
+ itemParcel.recycle();
+ ItemCallback.this.onItemLoaded(item);
+ }
+
+ @Override
+ public void onError(@NonNull String itemId) {
+ ItemCallback.this.onError(itemId);
+ }
+ }
}
interface MediaBrowserImpl {
@@ -473,7 +496,6 @@
}
static class MediaBrowserImplBase implements MediaBrowserImpl {
- private static final String TAG = "MediaBrowserCompat";
private static final boolean DBG = false;
private static final int CONNECT_STATE_DISCONNECTED = 0;
@@ -831,30 +853,30 @@
private final void onLoadChildren(final Messenger callback, final String parentId,
final List list) {
- // Check that there hasn't been a disconnect or a different ServiceConnection.
- if (!isCurrent(callback, "onLoadChildren")) {
- return;
- }
-
- List<MediaItem> data = list;
- if (DBG) {
- Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId);
- }
- if (data == null) {
- data = Collections.emptyList();
- }
-
- // Check that the subscription is still subscribed.
- final Subscription subscription = mSubscriptions.get(parentId);
- if (subscription == null) {
- if (DBG) {
- Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId);
+ // Check that there hasn't been a disconnect or a different ServiceConnection.
+ if (!isCurrent(callback, "onLoadChildren")) {
+ return;
}
- return;
- }
- // Tell the app.
- subscription.callback.onChildrenLoaded(parentId, data);
+ List<MediaItem> data = list;
+ if (DBG) {
+ Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId);
+ }
+ if (data == null) {
+ data = Collections.emptyList();
+ }
+
+ // Check that the subscription is still subscribed.
+ final Subscription subscription = mSubscriptions.get(parentId);
+ if (subscription == null) {
+ if (DBG) {
+ Log.d(TAG, "onLoadChildren for id that isn't subscribed id=" + parentId);
+ }
+ return;
+ }
+
+ // Tell the app.
+ subscription.callback.onChildrenLoaded(parentId, data);
}
/**
@@ -896,35 +918,33 @@
}
void connect() throws RemoteException {
- sendRequest(MediaBrowserServiceCompat.MSG_CONNECT, mContext.getPackageName(),
- mRootHints, mCallbacksMessenger);
+ sendRequest(CLIENT_MSG_CONNECT, mContext.getPackageName(), mRootHints,
+ mCallbacksMessenger);
}
void disconnect() throws RemoteException {
- sendRequest(MediaBrowserServiceCompat.MSG_DISCONNECT, null, null,
- mCallbacksMessenger);
+ sendRequest(CLIENT_MSG_DISCONNECT, null, null, mCallbacksMessenger);
}
void addSubscription(String parentId) throws RemoteException {
- sendRequest(MediaBrowserServiceCompat.MSG_ADD_SUBSCRIPTION, parentId, null,
- mCallbacksMessenger);
+ sendRequest(CLIENT_MSG_ADD_SUBSCRIPTION, parentId, null, mCallbacksMessenger);
}
void removeSubscription(String parentId) throws RemoteException {
- sendRequest(MediaBrowserServiceCompat.MSG_REMOVE_SUBSCRIPTION, parentId, null,
- mCallbacksMessenger);
+ sendRequest(CLIENT_MSG_REMOVE_SUBSCRIPTION, parentId, null, mCallbacksMessenger);
}
void getMediaItem(String mediaId, ResultReceiver receiver) throws RemoteException {
Bundle data = new Bundle();
- data.putParcelable(DATA_RESULT_RECEIVER, receiver);
- sendRequest(MediaBrowserServiceCompat.MSG_GET_MEDIA_ITEM, mediaId, data, null);
+ data.putParcelable(SERVICE_DATA_RESULT_RECEIVER, receiver);
+ sendRequest(CLIENT_MSG_GET_MEDIA_ITEM, mediaId, data, null);
}
private void sendRequest(int what, Object obj, Bundle data, Messenger cbMessenger)
throws RemoteException {
Message msg = Message.obtain();
msg.what = what;
+ msg.arg1 = CLIENT_VERSION_CURRENT;
msg.obj = obj;
msg.setData(data);
msg.replyTo = cbMessenger;
@@ -1023,22 +1043,23 @@
public void handleMessage(Message msg) {
Bundle data = msg.getData();
switch (msg.what) {
- case MSG_ON_CONNECT:
+ case SERVICE_MSG_ON_CONNECT:
onServiceConnected(mCallbacksMessenger, (String) msg.obj,
(MediaSessionCompat.Token) data.getParcelable(
- MediaBrowserServiceCompat.DATA_MEDIA_SESSION_TOKEN),
- data.getBundle(MediaBrowserServiceCompat.DATA_EXTRAS));
+ SERVICE_DATA_MEDIA_SESSION_TOKEN),
+ data.getBundle(SERVICE_DATA_EXTRAS));
break;
- case MSG_ON_CONNECT_FAILED:
+ case SERVICE_MSG_ON_CONNECT_FAILED:
onConnectionFailed(mCallbacksMessenger);
break;
- case MSG_ON_LOAD_CHILDREN:
+ case SERVICE_MSG_ON_LOAD_CHILDREN:
onLoadChildren(mCallbacksMessenger, (String) msg.obj,
- data.getParcelableArrayList(
- MediaBrowserServiceCompat.DATA_MEDIA_ITEM_LIST));
+ data.getParcelableArrayList(SERVICE_DATA_MEDIA_ITEM_LIST));
break;
default:
- super.handleMessage(msg);
+ Log.w(TAG, "Unhandled message: " + msg
+ + "\n Client version: " + CLIENT_VERSION_CURRENT
+ + "\n Service version: " + msg.arg1);
}
}
}
@@ -1054,7 +1075,9 @@
}
static class MediaBrowserImplApi21 implements MediaBrowserImpl {
- Object mBrowserObj;
+ protected Object mBrowserObj;
+ protected Messenger mMessenger;
+ protected Handler mHandler = new Handler();
public MediaBrowserImplApi21(Context context, ComponentName serviceComponent,
ConnectionCallback callback, Bundle rootHints) {
@@ -1113,9 +1136,93 @@
}
@Override
+ public void getItem(@NonNull final String mediaId, @NonNull final ItemCallback cb) {
+ if (TextUtils.isEmpty(mediaId)) {
+ throw new IllegalArgumentException("mediaId is empty.");
+ }
+ if (cb == null) {
+ throw new IllegalArgumentException("cb is null.");
+ }
+ if (!MediaBrowserCompatApi21.isConnected(mBrowserObj)) {
+ Log.i(TAG, "Not connected, unable to retrieve the MediaItem.");
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ cb.onError(mediaId);
+ }
+ });
+ return;
+ }
+ if (mMessenger == null) {
+ Bundle extras = MediaBrowserCompatApi21.getExtras(mBrowserObj);
+ IBinder serviceBinder = BundleCompat.getBinder(extras, EXTRA_MESSENGER_BINDER);
+ if (serviceBinder != null) {
+ mMessenger = new Messenger(serviceBinder);
+ }
+ }
+ if (mMessenger == null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Default framework implementation.
+ cb.onItemLoaded(null);
+ }
+ });
+ return;
+ }
+ ResultReceiver receiver = new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode != 0 || resultData == null
+ || !resultData.containsKey(MediaBrowserServiceCompat.KEY_MEDIA_ITEM)) {
+ cb.onError(mediaId);
+ return;
+ }
+ Parcelable item =
+ resultData.getParcelable(MediaBrowserServiceCompat.KEY_MEDIA_ITEM);
+ if (!(item instanceof MediaItem)) {
+ cb.onError(mediaId);
+ return;
+ }
+ cb.onItemLoaded((MediaItem)item);
+ }
+ };
+ try {
+ Bundle data = new Bundle();
+ data.putParcelable(SERVICE_DATA_RESULT_RECEIVER, receiver);
+ sendRequest(CLIENT_MSG_GET_MEDIA_ITEM, mediaId, data, null);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Remote error getting media item.");
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ cb.onError(mediaId);
+ }
+ });
+ }
+ }
+
+ private void sendRequest(int what, Object obj, Bundle data, Messenger cbMessenger)
+ throws RemoteException {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = CLIENT_VERSION_CURRENT;
+ msg.obj = obj;
+ msg.setData(data);
+ msg.replyTo = cbMessenger;
+ mMessenger.send(msg);
+ }
+ }
+
+ static class MediaBrowserImplApi23 extends MediaBrowserImplApi21 {
+ public MediaBrowserImplApi23(Context context, ComponentName serviceComponent,
+ ConnectionCallback callback, Bundle rootHints) {
+ super(context, serviceComponent, callback, rootHints);
+ }
+
+ @Override
public void getItem(@NonNull String mediaId, @NonNull ItemCallback cb) {
- // TODO Implement individual item loading on API 21-22 devices
- cb.onItemLoaded(null);
+ MediaBrowserCompatApi23.getItem(mBrowserObj, mediaId, cb.mItemCallbackObj);
}
}
}
diff --git a/v4/java/android/support/v4/media/MediaBrowserProtocol.java b/v4/java/android/support/v4/media/MediaBrowserProtocol.java
new file mode 100644
index 0000000..7c8feb3
--- /dev/null
+++ b/v4/java/android/support/v4/media/MediaBrowserProtocol.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.media;
+
+/***
+ * Defines the communication protocol for media browsers and media browser services.
+ * @hide
+ */
+class MediaBrowserProtocol {
+
+ /**
+ * MediaBrowserCompat will check the version of the connected MediaBrowserServiceCompat,
+ * and it will not send messages if they are introduced in the higher version of the
+ * MediaBrowserServiceCompat.
+ */
+ public static final int SERVICE_VERSION_1 = 1;
+ public static final int SERVICE_VERSION_CURRENT = SERVICE_VERSION_1;
+
+ /*
+ * Messages sent from the media browser service compat to the media browser compat.
+ * (Compat implementation for IMediaBrowserServiceCallbacks)
+ * DO NOT RENUMBER THESE!
+ */
+
+ /** (service v1)
+ * Sent after {@link MediaBrowserCompat#connect()} when the request has successfully
+ * completed.
+ * - arg1 : The service version
+ * - obj : The root media item id
+ * - data
+ * SERVICE_DATA_MEDIA_SESSION_TOKEN : Media session token
+ * SERVICE_DATA_EXTRAS : An extras bundle which contains EXTRA_SERVICE_VERSION
+ */
+ public static final int SERVICE_MSG_ON_CONNECT = 1;
+
+ /** (service v1)
+ * Sent after {@link MediaBrowserCompat#connect()} when the connection to the media browser
+ * failed.
+ * - arg1 : service version
+ */
+ public static final int SERVICE_MSG_ON_CONNECT_FAILED = 2;
+
+ /** (service v1)
+ * Sent when the list of children is loaded or updated.
+ * - arg1 : The service version
+ * - obj : The parent media item id
+ * - data
+ * SERVICE_DATA_MEDIA_ITEM_LIST : An array list for the media item children
+ */
+ public static final int SERVICE_MSG_ON_LOAD_CHILDREN = 3;
+
+ public static final String SERVICE_DATA_MEDIA_SESSION_TOKEN = "data_media_session_token";
+ public static final String SERVICE_DATA_EXTRAS = "data_extras";
+ public static final String SERVICE_DATA_MEDIA_ITEM_LIST = "data_media_item_list";
+ public static final String SERVICE_DATA_RESULT_RECEIVER = "data_result_receiver";
+
+ public static final String EXTRA_SERVICE_VERSION = "extra_service_version";
+ public static final String EXTRA_MESSENGER_BINDER = "extra_messenger";
+
+ /**
+ * MediaBrowserServiceCompat will check the version of the MediaBrowserCompat, and it will not
+ * send messages if they are introduced in the higher version of the MediaBrowserCompat.
+ */
+ public static final int CLIENT_VERSION_1 = 1;
+ public static final int CLIENT_VERSION_CURRENT = CLIENT_VERSION_1;
+
+ /*
+ * Messages sent from the media browser compat to the media browser service compat.
+ * (Compat implementation for IMediaBrowserService)
+ * DO NOT RENUMBER THESE!
+ */
+
+ /** (client v1)
+ * Sent to connect to the media browse service compat.
+ * - arg1 : The client version
+ * - obj : The package name
+ * - data : An optional root hints bundle of service-specific arguments
+ * - replayTo : Client messenger
+ */
+ public static final int CLIENT_MSG_CONNECT = 1;
+
+ /** (client v1)
+ * Sent to disconnect from the media browse service compat.
+ * - arg1 : The client version
+ * - replayTo : Client messenger
+ */
+ public static final int CLIENT_MSG_DISCONNECT = 2;
+
+ /** (client v1)
+ * Sent to subscribe for changes to the children of the specified media id.
+ * - arg1 : The client version
+ * - obj : The media item id
+ * - replayTo : Client messenger
+ */
+ public static final int CLIENT_MSG_ADD_SUBSCRIPTION = 3;
+
+ /** (client v1)
+ * Sent to unsubscribe for changes to the children of the specified media id.
+ * - arg1 : The client version
+ * - obj : The media item id
+ * - replayTo : Client messenger
+ */
+ public static final int CLIENT_MSG_REMOVE_SUBSCRIPTION = 4;
+
+ /** (client v1)
+ * Sent to retrieves a specific media item from the connected service.
+ * - arg1 : The client version
+ * - obj : The media item id
+ * - data
+ * SERVICE_DATA_RESULT_RECEIVER : Result receiver to get the result
+ */
+ public static final int CLIENT_MSG_GET_MEDIA_ITEM = 5;
+}
diff --git a/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
index ded7e7e..c618e4b 100644
--- a/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -30,6 +30,7 @@
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.app.BundleCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.os.ResultReceiver;
import android.support.v4.util.ArrayMap;
@@ -42,6 +43,8 @@
import java.util.HashSet;
import java.util.List;
+import static android.support.v4.media.MediaBrowserProtocol.*;
+
/**
* Base class for media browse services.
* <p>
@@ -68,16 +71,6 @@
private static final String TAG = "MediaBrowserServiceCompat";
private static final boolean DBG = false;
- public static final String DATA_MEDIA_SESSION_TOKEN = "data_media_session_token";
- public static final String DATA_EXTRAS = "data_extras";
- public static final String DATA_MEDIA_ITEM_LIST = "data_media_item_list";
-
- public static final int MSG_CONNECT = 1;
- public static final int MSG_DISCONNECT = 2;
- public static final int MSG_ADD_SUBSCRIPTION = 3;
- public static final int MSG_REMOVE_SUBSCRIPTION = 4;
- public static final int MSG_GET_MEDIA_ITEM = 5;
-
private MediaBrowserServiceImpl mImpl;
/**
@@ -124,8 +117,7 @@
@Override
public void onCreate() {
mServiceObj = MediaBrowserServiceCompatApi21.createService();
- MediaBrowserServiceCompatApi21.onCreate(mServiceObj,
- new ServiceImplApi21(mHandler.getServiceImpl()));
+ MediaBrowserServiceCompatApi21.onCreate(mServiceObj, new ServiceImplApi21());
}
@Override
@@ -134,33 +126,50 @@
}
}
+ class MediaBrowserServiceImplApi23 implements MediaBrowserServiceImpl {
+ private Object mServiceObj;
+
+ @Override
+ public void onCreate() {
+ mServiceObj = MediaBrowserServiceCompatApi23.createService();
+ MediaBrowserServiceCompatApi23.onCreate(mServiceObj, new ServiceImplApi23());
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return MediaBrowserServiceCompatApi23.onBind(mServiceObj, intent);
+ }
+ }
+
private final class ServiceHandler extends Handler {
private final ServiceImpl mServiceImpl = new ServiceImpl();
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_CONNECT:
+ case CLIENT_MSG_CONNECT:
mServiceImpl.connect((String) msg.obj, msg.getData(),
new ServiceCallbacksCompat(msg.replyTo));
break;
- case MSG_DISCONNECT:
+ case CLIENT_MSG_DISCONNECT:
mServiceImpl.disconnect(new ServiceCallbacksCompat(msg.replyTo));
break;
- case MSG_ADD_SUBSCRIPTION:
+ case CLIENT_MSG_ADD_SUBSCRIPTION:
mServiceImpl.addSubscription((String) msg.obj,
new ServiceCallbacksCompat(msg.replyTo));
break;
- case MSG_REMOVE_SUBSCRIPTION:
+ case CLIENT_MSG_REMOVE_SUBSCRIPTION:
mServiceImpl.removeSubscription((String) msg.obj,
new ServiceCallbacksCompat(msg.replyTo));
break;
- case MSG_GET_MEDIA_ITEM:
+ case CLIENT_MSG_GET_MEDIA_ITEM:
mServiceImpl.getMediaItem((String) msg.obj, (ResultReceiver) msg.getData()
- .getParcelable(MediaBrowserCompat.DATA_RESULT_RECEIVER));
+ .getParcelable(SERVICE_DATA_RESULT_RECEIVER));
break;
default:
- super.handleMessage(msg);
+ Log.w(TAG, "Unhandled message: " + msg
+ + "\n Service version: " + SERVICE_VERSION_CURRENT
+ + "\n Client version: " + msg.arg1);
}
}
@@ -371,11 +380,11 @@
}
}
- private class ServiceImplApi21 implements MediaBrowserServiceCompatApi21.ServiceImpl {
+ private class ServiceImplApi21 implements MediaBrowserServiceCompatApi21.ServiceImplApi21 {
final ServiceImpl mServiceImpl;
- ServiceImplApi21(ServiceImpl serviceImpl) {
- mServiceImpl = serviceImpl;
+ ServiceImplApi21() {
+ mServiceImpl = mHandler.getServiceImpl();
}
@Override
@@ -401,13 +410,23 @@
final MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
mServiceImpl.removeSubscription(id, new ServiceCallbacksApi21(callbacks));
}
+ }
+ private class ServiceImplApi23 extends ServiceImplApi21
+ implements MediaBrowserServiceCompatApi23.ServiceImplApi23 {
@Override
- public void getMediaItem(final String mediaId, final android.os.ResultReceiver receiver) {
+ public void getMediaItem(final String mediaId,
+ final MediaBrowserServiceCompatApi23.ItemCallback cb) {
ResultReceiver receiverCompat = new ResultReceiver(mHandler) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
- receiver.send(resultCode, resultData);
+ MediaBrowserCompat.MediaItem item = resultData.getParcelable(KEY_MEDIA_ITEM);
+ Parcel itemParcel = null;
+ if (item != null) {
+ itemParcel = Parcel.obtain();
+ item.writeToParcel(itemParcel, 0);
+ }
+ cb.onItemLoaded(resultCode, resultData, itemParcel);
}
};
mServiceImpl.getMediaItem(mediaId, receiverCompat);
@@ -436,27 +455,33 @@
public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
throws RemoteException {
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
Bundle data = new Bundle();
- data.putParcelable(DATA_MEDIA_SESSION_TOKEN, session);
- data.putBundle(DATA_EXTRAS, extras);
- sendRequest(MediaBrowserCompat.MSG_ON_CONNECT, root, data);
+ data.putParcelable(SERVICE_DATA_MEDIA_SESSION_TOKEN, session);
+ data.putBundle(SERVICE_DATA_EXTRAS, extras);
+ sendRequest(SERVICE_MSG_ON_CONNECT, root, data);
}
public void onConnectFailed() throws RemoteException {
- sendRequest(MediaBrowserCompat.MSG_ON_CONNECT_FAILED, null, null);
+ sendRequest(SERVICE_MSG_ON_CONNECT_FAILED, null, null);
}
public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
throws RemoteException {
Bundle data = new Bundle();
- data.putParcelableArrayList(DATA_MEDIA_ITEM_LIST,
+ data.putParcelableArrayList(SERVICE_DATA_MEDIA_ITEM_LIST,
list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
- sendRequest(MediaBrowserCompat.MSG_ON_LOAD_CHILDREN, mediaId, data);
+ sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, mediaId, data);
}
- private void sendRequest(int what, Object obj, Bundle data) throws RemoteException {
+ private void sendRequest(int what, Object obj, Bundle data)
+ throws RemoteException {
Message msg = Message.obtain();
msg.what = what;
+ msg.arg1 = SERVICE_VERSION_CURRENT;
msg.obj = obj;
msg.setData(data);
mCallbacks.send(msg);
@@ -465,6 +490,7 @@
private class ServiceCallbacksApi21 implements ServiceCallbacks {
final MediaBrowserServiceCompatApi21.ServiceCallbacks mCallbacks;
+ Messenger mMessenger;
ServiceCallbacksApi21(MediaBrowserServiceCompatApi21.ServiceCallbacks callbacks) {
mCallbacks = callbacks;
@@ -476,6 +502,12 @@
public void onConnect(String root, MediaSessionCompat.Token session, Bundle extras)
throws RemoteException {
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ mMessenger = new Messenger(mHandler);
+ BundleCompat.putBinder(extras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
+ extras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
mCallbacks.onConnect(root, session.getToken(), extras);
}
@@ -501,7 +533,9 @@
@Override
public void onCreate() {
super.onCreate();
- if (Build.VERSION.SDK_INT >= 21) {
+ if (Build.VERSION.SDK_INT >= 23) {
+ mImpl = new MediaBrowserServiceImplApi23();
+ } else if (Build.VERSION.SDK_INT >= 21) {
mImpl = new MediaBrowserServiceImplApi21();
} else {
mImpl = new MediaBrowserServiceImplBase();