Merge changes from topics "mediasession2_libraryroot", "mediasession2_commandbutton", "medianotification_updatable"

* changes:
  MediaSession2: Rename BrowserRoot to LibraryRoot and move to updatable
  MediaSession2: Move MediaSession2.CommandButton to updatable
  MediaSession2: Move MediaSessionService2.MediaNotification to updatable
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index 5ad4313..5cb8313 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -19,7 +19,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.media.update.ApiLoader;
 import android.media.update.MediaBrowser2Provider;
@@ -41,14 +40,14 @@
      */
     public static class BrowserCallback extends MediaController2.ControllerCallback {
         /**
-         * Called with the result of {@link #getBrowserRoot(Bundle)}.
+         * Called with the result of {@link #getLibraryRoot(Bundle)}.
          * <p>
-         * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't
+         * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the library root isn't
          * available.
          *
          * @param rootHints rootHints that you previously requested.
-         * @param rootMediaId media id of the browser root. Can be {@code null}
-         * @param rootExtra extra of the browser root. Can be {@code null}
+         * @param rootMediaId media id of the library root. Can be {@code null}
+         * @param rootExtra extra of the library root. Can be {@code null}
          */
         public void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId,
                 @Nullable Bundle rootExtra) { }
@@ -114,8 +113,15 @@
                 .createMediaBrowser2(context, this, token, executor, (BrowserCallback) callback);
     }
 
-    public void getBrowserRoot(Bundle rootHints) {
-        mProvider.getBrowserRoot_impl(rootHints);
+    /**
+     * Get the library root. Result would be sent back asynchronously with the
+     * {@link BrowserCallback#onGetRootResult(Bundle, String, Bundle)}.
+     *
+     * @param rootHints hint for the root
+     * @see BrowserCallback#onGetRootResult(Bundle, String, Bundle)
+     */
+    public void getLibraryRoot(Bundle rootHints) {
+        mProvider.getLibraryRoot_impl(rootHints);
     }
 
     /**
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index a901c68..f88f9f2 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -25,6 +25,7 @@
 import android.media.MediaSession2.BuilderBase;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
 import android.media.update.MediaLibraryService2Provider.MediaLibrarySessionProvider;
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSessionService2Provider;
@@ -116,15 +117,15 @@
          *
          * @param controllerInfo information of the controller requesting access to browse media.
          * @param rootHints An optional bundle of service-specific arguments to send
-         * to the media browser service when connecting and retrieving the
+         * to the media library service when connecting and retrieving the
          * root id for browsing, or null if none. The contents of this
          * bundle may affect the information returned when browsing.
-         * @return The {@link BrowserRoot} for accessing this app's content or null.
-         * @see BrowserRoot#EXTRA_RECENT
-         * @see BrowserRoot#EXTRA_OFFLINE
-         * @see BrowserRoot#EXTRA_SUGGESTED
+         * @return The {@link LibraryRoot} for accessing this app's content or null.
+         * @see LibraryRoot#EXTRA_RECENT
+         * @see LibraryRoot#EXTRA_OFFLINE
+         * @see LibraryRoot#EXTRA_SUGGESTED
          */
-        public @Nullable BrowserRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
+        public @Nullable LibraryRoot onGetRoot(@NonNull ControllerInfo controllerInfo,
                 @Nullable Bundle rootHints) {
             return null;
         }
@@ -237,17 +238,17 @@
     public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
 
     /**
-     * Contains information that the browser service needs to send to the client
-     * when first connected.
+     * Contains information that the library service needs to send to the client when
+     * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called.
      */
-    public static final class BrowserRoot {
+    public static final class LibraryRoot {
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for recently played media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * librar root for recently played media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
+         * <p>When creating a media browser for a given media library service, this key can be
          * supplied as a root hint for retrieving media items that are recently played.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
          * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
          *
@@ -259,13 +260,13 @@
         public static final String EXTRA_RECENT = "android.media.extra.RECENT";
 
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for offline media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * library root for offline media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
+         * <p>When creating a media browser for a given media library service, this key can be
          * supplied as a root hint for retrieving media items that are can be played without an
          * internet connection.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
          * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
          *
@@ -277,14 +278,14 @@
         public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
 
         /**
-         * The lookup key for a boolean that indicates whether the browser service should return a
-         * browser root for suggested media items.
+         * The lookup key for a boolean that indicates whether the library service should return a
+         * library root for suggested media items.
          *
-         * <p>When creating a media browser for a given media browser service, this key can be
-         * supplied as a root hint for retrieving the media items suggested by the media browser
+         * <p>When creating a media browser for a given media library service, this key can be
+         * supplied as a root hint for retrieving the media items suggested by the media library
          * service. The list of media items is considered ordered by relevance, first being the top
          * suggestion.
-         * If the media browser service can provide such media items, the implementation must return
+         * If the media library service can provide such media items, the implementation must return
          * the key in the root hint when
          * {@link MediaLibrarySessionCallback#onGetRoot(ControllerInfo, Bundle)} is called back.
          *
@@ -295,35 +296,31 @@
          */
         public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
 
-        final private String mRootId;
-        final private Bundle mExtras;
+        private final LibraryRootProvider mProvider;
 
         /**
-         * Constructs a browser root.
+         * Constructs a library root.
          * @param rootId The root id for browsing.
-         * @param extras Any extras about the browser service.
+         * @param extras Any extras about the library service.
          */
-        public BrowserRoot(@NonNull String rootId, @Nullable Bundle extras) {
-            if (rootId == null) {
-                throw new IllegalArgumentException("The root id in BrowserRoot cannot be null. " +
-                        "Use null for BrowserRoot instead.");
-            }
-            mRootId = rootId;
-            mExtras = extras;
+        public LibraryRoot(@NonNull Context context,
+                @NonNull String rootId, @Nullable Bundle extras) {
+            mProvider = ApiLoader.getProvider(context).createMediaLibraryService2LibraryRoot(
+                    context, this, rootId, extras);
         }
 
         /**
          * Gets the root id for browsing.
          */
         public String getRootId() {
-            return mRootId;
+            return mProvider.getRootId_impl();
         }
 
         /**
-         * Gets any extras about the browser service.
+         * Gets any extras about the library service.
          */
         public Bundle getExtras() {
-            return mExtras;
+            return mProvider.getExtras_impl();
         }
     }
 }
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index acf9615..5670bd8 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -31,6 +31,7 @@
 import android.media.update.ApiLoader;
 import android.media.update.MediaSession2Provider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider;
 import android.media.update.MediaSession2Provider.CommandGroupProvider;
 import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
@@ -768,32 +769,15 @@
      * <p>
      * It's up to the controller's decision to respect or ignore this customization request.
      */
-    // TODO(jaewan): Move this to updatable.
     public static class CommandButton {
-        private static final String KEY_COMMAND
-                = "android.media.media_session2.command_button.command";
-        private static final String KEY_ICON_RES_ID
-                = "android.media.media_session2.command_button.icon_res_id";
-        private static final String KEY_DISPLAY_NAME
-                = "android.media.media_session2.command_button.display_name";
-        private static final String KEY_EXTRA
-                = "android.media.media_session2.command_button.extra";
-        private static final String KEY_ENABLED
-                = "android.media.media_session2.command_button.enabled";
+        private final CommandButtonProvider mProvider;
 
-        private Command mCommand;
-        private int mIconResId;
-        private String mDisplayName;
-        private Bundle mExtra;
-        private boolean mEnabled;
-
-        private CommandButton(@Nullable Command command, int iconResId,
-                @Nullable String displayName, Bundle extra, boolean enabled) {
-            mCommand = command;
-            mIconResId = iconResId;
-            mDisplayName = displayName;
-            mExtra = extra;
-            mEnabled = enabled;
+        /**
+         * @hide
+         */
+        @SystemApi
+        public CommandButton(CommandButtonProvider provider) {
+            mProvider = provider;
         }
 
         /**
@@ -803,7 +787,7 @@
          * @return command or {@code null}
          */
         public @Nullable Command getCommand() {
-            return mCommand;
+            return mProvider.getCommand_impl();
         }
 
         /**
@@ -813,7 +797,7 @@
          * @return resource id of the icon. Can be {@code 0}.
          */
         public int getIconResId() {
-            return mIconResId;
+            return mProvider.getIconResId_impl();
         }
 
         /**
@@ -823,7 +807,7 @@
          * @return custom display name. Can be {@code null} or empty.
          */
         public @Nullable String getDisplayName() {
-            return mDisplayName;
+            return mProvider.getDisplayName_impl();
         }
 
         /**
@@ -832,7 +816,7 @@
          * @return
          */
         public @Nullable Bundle getExtra() {
-            return mExtra;
+            return mProvider.getExtra_impl();
         }
 
         /**
@@ -841,92 +825,50 @@
          * @return {@code true} if enabled. {@code false} otherwise.
          */
         public boolean isEnabled() {
-            return mEnabled;
+            return mProvider.isEnabled_impl();
         }
 
         /**
          * @hide
          */
-        // TODO(jaewan): @SystemApi
-        public @NonNull Bundle toBundle() {
-            Bundle bundle = new Bundle();
-            bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
-            bundle.putInt(KEY_ICON_RES_ID, mIconResId);
-            bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
-            bundle.putBundle(KEY_EXTRA, mExtra);
-            bundle.putBoolean(KEY_ENABLED, mEnabled);
-            return bundle;
-        }
-
-        /**
-         * @hide
-         */
-        // TODO(jaewan): @SystemApi
-        public static @Nullable CommandButton fromBundle(Context context, Bundle bundle) {
-            Builder builder = new Builder();
-            builder.setCommand(Command.fromBundle(context, bundle.getBundle(KEY_COMMAND)));
-            builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
-            builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
-            builder.setExtra(bundle.getBundle(KEY_EXTRA));
-            builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
-            try {
-                return builder.build();
-            } catch (IllegalStateException e) {
-                // Malformed or version mismatch. Return null for now.
-                return null;
-            }
+        @SystemApi
+        public CommandButtonProvider getProvider() {
+            return mProvider;
         }
 
         /**
          * Builder for {@link CommandButton}.
          */
         public static class Builder {
-            private Command mCommand;
-            private int mIconResId;
-            private String mDisplayName;
-            private Bundle mExtra;
-            private boolean mEnabled;
+            private final CommandButtonProvider.BuilderProvider mProvider;
 
-            public Builder() {
-                mEnabled = true;
+            public Builder(@NonNull Context context) {
+                mProvider = ApiLoader.getProvider(context)
+                        .createMediaSession2CommandButtonBuilder(context, this);
             }
 
             public Builder setCommand(Command command) {
-                mCommand = command;
-                return this;
+                return mProvider.setCommand_impl(command);
             }
 
             public Builder setIconResId(int resId) {
-                mIconResId = resId;
-                return this;
+                return mProvider.setIconResId_impl(resId);
             }
 
             public Builder setDisplayName(String displayName) {
-                mDisplayName = displayName;
-                return this;
+                return mProvider.setDisplayName_impl(displayName);
             }
 
             public Builder setEnabled(boolean enabled) {
-                mEnabled = enabled;
-                return this;
+                return mProvider.setEnabled_impl(enabled);
             }
 
             public Builder setExtra(Bundle extra) {
-                mExtra = extra;
-                return this;
+                return mProvider.setExtra_impl(extra);
             }
 
             public CommandButton build() {
-                if (mEnabled && mCommand == null) {
-                    throw new IllegalStateException("Enabled button needs Command"
-                            + " for controller to invoke the command");
-                }
-                if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
-                        && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
-                    throw new IllegalStateException("Custom commands needs icon and"
-                            + " and name to display");
-                }
-                return new CommandButton(mCommand, mIconResId, mDisplayName, mExtra, mEnabled);
+                return mProvider.build_impl();
             }
         }
     }
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
index 6b2de06..0b5dddf 100644
--- a/media/java/android/media/MediaSessionService2.java
+++ b/media/java/android/media/MediaSessionService2.java
@@ -21,10 +21,12 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
 import android.media.update.MediaSessionService2Provider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
 import android.os.IBinder;
 
 /**
@@ -165,7 +167,6 @@
      * @param state playback state
      * @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
      */
-    // TODO(jaewan): Also add metadata
     public MediaNotification onUpdateNotification(PlaybackState2 state) {
         return mProvider.onUpdateNotification_impl(state);
     }
@@ -204,31 +205,31 @@
      * foreground service to keep playback running in the background. It's highly recommended to
      * show media style notification here.
      */
-    // TODO(jaewan): Should we also move this to updatable?
     public static class MediaNotification {
-        public final int id;
-        public final Notification notification;
-
-        private MediaNotification(int id, @NonNull Notification notification) {
-            this.id = id;
-            this.notification = notification;
-        }
+        private final MediaNotificationProvider mProvider;
 
         /**
-         * Create a {@link MediaNotification}.
+         * Default constructor
          *
+         * @param context context
          * @param notificationId notification id to be used for
          *      {@link android.app.NotificationManager#notify(int, Notification)}.
          * @param notification a notification to make session service foreground service. Media
          *      style notification is recommended here.
-         * @return
          */
-        public static MediaNotification create(int notificationId,
-                @NonNull Notification notification) {
-            if (notification == null) {
-                throw new IllegalArgumentException("Notification cannot be null");
-            }
-            return new MediaNotification(notificationId, notification);
+        public MediaNotification(@NonNull Context context,
+                int notificationId, @NonNull Notification notification) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSessionService2MediaNotification(
+                            context, this, notificationId, notification);
+        }
+
+        public int getNotificationId() {
+            return mProvider.getNotificationId_impl();
+        }
+
+        public Notification getNotification() {
+            return mProvider.getNotification_impl();
         }
     }
 }
diff --git a/media/java/android/media/update/MediaBrowser2Provider.java b/media/java/android/media/update/MediaBrowser2Provider.java
index 67680c7..17256a8 100644
--- a/media/java/android/media/update/MediaBrowser2Provider.java
+++ b/media/java/android/media/update/MediaBrowser2Provider.java
@@ -23,7 +23,7 @@
  * @hide
  */
 public interface MediaBrowser2Provider extends MediaController2Provider {
-    void getBrowserRoot_impl(Bundle rootHints);
+    void getLibraryRoot_impl(Bundle rootHints);
 
     void subscribe_impl(String parentId, Bundle options);
     void unsubscribe_impl(String parentId, Bundle options);
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
index 87f509a..923551a 100644
--- a/media/java/android/media/update/MediaLibraryService2Provider.java
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -33,4 +33,9 @@
         void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, Bundle options);
         void notifyChildrenChanged_impl(String parentId, Bundle options);
     }
+
+    interface LibraryRootProvider {
+        String getRootId_impl();
+        Bundle getExtras_impl();
+    }
 }
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index da4d0c7..9abf34a 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -24,6 +24,7 @@
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
+import android.media.MediaSession2.CommandButton.Builder;
 import android.media.MediaSession2.CommandGroup;
 import android.media.MediaSession2.ControllerInfo;
 import android.media.MediaSession2.PlaylistParams;
@@ -82,6 +83,23 @@
         Bundle toBundle_impl();
     }
 
+    interface CommandButtonProvider {
+        Command getCommand_impl();
+        int getIconResId_impl();
+        String getDisplayName_impl();
+        Bundle getExtra_impl();
+        boolean isEnabled_impl();
+
+        interface BuilderProvider {
+            Builder setCommand_impl(Command command);
+            Builder setIconResId_impl(int resId);
+            Builder setDisplayName_impl(String displayName);
+            Builder setEnabled_impl(boolean enabled);
+            Builder setExtra_impl(Bundle extra);
+            CommandButton build_impl();
+        }
+    }
+
     interface ControllerInfoProvider {
         String getPackageName_impl();
         int getUid_impl();
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
index 9455da7..42e7587 100644
--- a/media/java/android/media/update/MediaSessionService2Provider.java
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -17,6 +17,7 @@
 package android.media.update;
 
 import android.annotation.SystemApi;
+import android.app.Notification;
 import android.content.Intent;
 import android.media.MediaSession2;
 import android.media.MediaSessionService2.MediaNotification;
@@ -33,4 +34,9 @@
     // Service
     void onCreate_impl();
     IBinder onBind_impl(Intent intent);
+
+    interface MediaNotificationProvider {
+        int getNotificationId_impl();
+        Notification getNotification_impl();
+    }
 }
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 922b452..862a402 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,6 +17,7 @@
 package android.media.update;
 
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.content.Context;
 import android.media.DataSourceDesc;
 import android.media.MediaBrowser2;
@@ -25,25 +26,31 @@
 import android.media.MediaController2.ControllerCallback;
 import android.media.MediaItem2;
 import android.media.MediaLibraryService2;
+import android.media.MediaLibraryService2.LibraryRoot;
 import android.media.MediaLibraryService2.MediaLibrarySession;
 import android.media.MediaLibraryService2.MediaLibrarySessionBuilder;
 import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerInterface;
 import android.media.MediaSession2;
+import android.media.MediaSession2.CommandButton.Builder;
 import android.media.MediaSession2.PlaylistParams;
 import android.media.MediaSession2.SessionCallback;
 import android.media.MediaSessionService2;
+import android.media.MediaSessionService2.MediaNotification;
 import android.media.PlaybackState2;
 import android.media.Rating2;
 import android.media.SessionPlayer2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
+import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
 import android.media.update.MediaSession2Provider.BuilderBaseProvider;
+import android.media.update.MediaSession2Provider.CommandButtonProvider.BuilderProvider;
 import android.media.update.MediaSession2Provider.CommandGroupProvider;
 import android.media.update.MediaSession2Provider.CommandProvider;
 import android.media.update.MediaSession2Provider.ControllerInfoProvider;
 import android.media.update.MediaSession2Provider.PlaylistParamsProvider;
+import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
 import android.os.Bundle;
 import android.os.IInterface;
 import android.util.AttributeSet;
@@ -79,6 +86,7 @@
             PlaylistParams playlistParams, int repeatMode, int shuffleMode,
             MediaMetadata2 playlistMetadata);
     PlaylistParams fromBundle_PlaylistParams(Context context, Bundle bundle);
+    BuilderProvider createMediaSession2CommandButtonBuilder(Context context, Builder builder);
     BuilderBaseProvider<MediaSession2, SessionCallback> createMediaSession2Builder(
             Context context, MediaSession2.Builder instance, MediaPlayerInterface player);
 
@@ -89,12 +97,16 @@
             SessionToken2 token, Executor executor, BrowserCallback callback);
 
     MediaSessionService2Provider createMediaSessionService2(MediaSessionService2 instance);
+    MediaNotificationProvider createMediaSessionService2MediaNotification(Context context,
+            MediaNotification mediaNotification, int notificationId, Notification notification);
 
     MediaSessionService2Provider createMediaLibraryService2(MediaLibraryService2 instance);
     BuilderBaseProvider<MediaLibrarySession, MediaLibrarySessionCallback>
         createMediaLibraryService2Builder(
             Context context, MediaLibrarySessionBuilder instance, MediaPlayerInterface player,
             Executor callbackExecutor, MediaLibrarySessionCallback callback);
+    LibraryRootProvider createMediaLibraryService2LibraryRoot(Context context, LibraryRoot instance,
+            String rootId, Bundle extras);
 
     SessionToken2Provider createSessionToken2(Context context, SessionToken2 instance,
             String packageName, String serviceName, int uid);