MediaBrowser: Handle null results properly in onLoadChildren

- Allow to send results with null for notifying an error on loading
  media items.
- Add a workaround for the existing bug among API 21 - 23.

Bug: 19127753
Change-Id: I1507cf7b55fbc7187cf77801c3370ebb4cc3f614
diff --git a/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java
index b285296..c6ccb14 100644
--- a/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/v4/api21/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -27,6 +27,8 @@
 import java.util.List;
 
 class MediaBrowserCompatApi21 {
+    static final String NULL_MEDIA_ITEM_ID =
+            "android.support.v4.media.MediaBrowserCompat.NULL_MEDIA_ITEM";
 
     public static Object createConnectionCallback(ConnectionCallback callback) {
         return new ConnectionCallbackProxy<>(callback);
@@ -112,7 +114,7 @@
     }
 
     interface SubscriptionCallback {
-        void onChildrenLoaded(@NonNull String parentId, @NonNull List<Parcel> children);
+        void onChildrenLoaded(@NonNull String parentId, List<Parcel> children);
         void onError(@NonNull String parentId);
     }
 
@@ -126,8 +128,12 @@
 
         @Override
         public void onChildrenLoaded(@NonNull String parentId,
-                @NonNull List<MediaBrowser.MediaItem> children) {
+                List<MediaBrowser.MediaItem> children) {
             List<Parcel> parcelList = null;
+            if (children != null && children.size() == 1
+                    && children.get(0).getMediaId().equals(NULL_MEDIA_ITEM_ID)) {
+                children = null;
+            }
             if (children != null) {
                 parcelList = new ArrayList<>();
                 for (MediaBrowser.MediaItem item : children) {
diff --git a/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java b/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
index 1baa14c..a1506d3 100644
--- a/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
+++ b/v4/api21/android/support/v4/media/MediaBrowserServiceCompatApi21.java
@@ -18,8 +18,10 @@
 
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.media.MediaDescription;
 import android.media.browse.MediaBrowser;
 import android.media.session.MediaSession;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -61,6 +63,16 @@
     }
 
     public static class ServiceCallbacksApi21 implements ServiceCallbacks {
+        private static final ParceledListSlice sNullParceledListSlice;
+        static {
+            MediaDescription nullDescription = new MediaDescription.Builder().setMediaId(
+                    MediaBrowserCompatApi21.NULL_MEDIA_ITEM_ID).build();
+            MediaBrowser.MediaItem nullMediaItem = new MediaBrowser.MediaItem(nullDescription, 0);
+            List<MediaBrowser.MediaItem> nullMediaItemList = new ArrayList<>();
+            nullMediaItemList.add(nullMediaItem);
+            sNullParceledListSlice = new ParceledListSlice(nullMediaItemList);
+        }
+
         private final IMediaBrowserServiceCallbacks mCallbacks;
 
         ServiceCallbacksApi21(IMediaBrowserServiceCallbacks callbacks) {
@@ -89,7 +101,12 @@
                     parcel.recycle();
                 }
             }
-            final ParceledListSlice<MediaBrowser.MediaItem> pls = new ParceledListSlice(itemList);
+            ParceledListSlice<MediaBrowser.MediaItem> pls;
+            if (Build.VERSION.SDK_INT > 23) {
+                pls = itemList == null ? null : new ParceledListSlice(itemList);
+            } else {
+                pls = itemList == null ? sNullParceledListSlice : new ParceledListSlice(itemList);
+            }
             mCallbacks.onLoadChildren(mediaId, pls);
         }
     }
diff --git a/v4/java/android/support/v4/media/MediaBrowserCompat.java b/v4/java/android/support/v4/media/MediaBrowserCompat.java
index 18da6d5..dcce7f5 100644
--- a/v4/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/v4/java/android/support/v4/media/MediaBrowserCompat.java
@@ -42,7 +42,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import static android.support.v4.media.MediaBrowserProtocol.*;
@@ -394,10 +393,9 @@
          * Called when the list of children is loaded or updated.
          *
          * @param parentId The media id of the parent media item.
-         * @param children The children which were loaded.
+         * @param children The children which were loaded, or null if the id is invalid.
          */
-        public void onChildrenLoaded(@NonNull String parentId,
-                                     @NonNull List<MediaItem> children) {
+        public void onChildrenLoaded(@NonNull String parentId, List<MediaItem> children) {
         }
 
         /**
@@ -862,9 +860,6 @@
             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);
diff --git a/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
index e2363cd..205e789 100644
--- a/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -470,9 +470,12 @@
 
         public void onLoadChildren(String mediaId, List<MediaBrowserCompat.MediaItem> list)
                 throws RemoteException {
-            Bundle data = new Bundle();
-            data.putParcelableArrayList(SERVICE_DATA_MEDIA_ITEM_LIST,
-                    list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
+            Bundle data = null;
+            if (list != null) {
+                data = new Bundle();
+                data.putParcelableArrayList(SERVICE_DATA_MEDIA_ITEM_LIST,
+                        list instanceof ArrayList ? (ArrayList) list : new ArrayList<>(list));
+            }
             sendRequest(SERVICE_MSG_ON_LOAD_CHILDREN, mediaId, data);
         }
 
@@ -716,10 +719,6 @@
                 = new Result<List<MediaBrowserCompat.MediaItem>>(parentId) {
             @Override
             void onResultSent(List<MediaBrowserCompat.MediaItem> list) {
-                if (list == null) {
-                    throw new IllegalStateException("onLoadChildren sent null list for id "
-                            + parentId);
-                }
                 if (mConnections.get(connection.callbacks.asBinder()) != connection) {
                     if (DBG) {
                         Log.d(TAG, "Not sending onLoadChildren result for connection that has"