Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.media;
     18 
     19 import static android.support.v4.media.MediaBrowserCompat.EXTRA_PAGE;
     20 import static android.support.v4.media.MediaBrowserCompat.EXTRA_PAGE_SIZE;
     21 
     22 import static androidx.media.MediaConstants2.ARGUMENT_EXTRAS;
     23 import static androidx.media.MediaConstants2.ARGUMENT_PAGE;
     24 import static androidx.media.MediaConstants2.ARGUMENT_PAGE_SIZE;
     25 
     26 import android.app.PendingIntent;
     27 import android.content.Intent;
     28 import android.os.BadParcelableException;
     29 import android.os.Bundle;
     30 import android.os.IBinder;
     31 import android.support.v4.media.MediaBrowserCompat.MediaItem;
     32 
     33 import androidx.annotation.NonNull;
     34 import androidx.annotation.Nullable;
     35 import androidx.media.MediaLibraryService2.MediaLibrarySession.Builder;
     36 import androidx.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
     37 import androidx.media.MediaSession2.ControllerInfo;
     38 
     39 import java.util.List;
     40 import java.util.concurrent.Executor;
     41 
     42 /**
     43  * Base class for media library services.
     44  * <p>
     45  * Media library services enable applications to browse media content provided by an application
     46  * and ask the application to start playing it. They may also be used to control content that
     47  * is already playing by way of a {@link MediaSession2}.
     48  * <p>
     49  * When extending this class, also add the following to your {@code AndroidManifest.xml}.
     50  * <pre>
     51  * &lt;service android:name="component_name_of_your_implementation" &gt;
     52  *   &lt;intent-filter&gt;
     53  *     &lt;action android:name="android.media.MediaLibraryService2" /&gt;
     54  *   &lt;/intent-filter&gt;
     55  * &lt;/service&gt;</pre>
     56  * <p>
     57  * The {@link MediaLibraryService2} class derives from {@link MediaSessionService2}. IDs shouldn't
     58  * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
     59  * default, an empty string will be used for ID of the service. If you want to specify an ID,
     60  * declare metadata in the manifest as follows.
     61  *
     62  * @see MediaSessionService2
     63  */
     64 public abstract class MediaLibraryService2 extends MediaSessionService2 {
     65     /**
     66      * This is the interface name that a service implementing a session service should say that it
     67      * support -- that is, this is the action it uses for its intent filter.
     68      */
     69     public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
     70 
     71     /**
     72      * Session for the {@link MediaLibraryService2}. Build this object with
     73      * {@link Builder} and return in {@link #onCreateSession(String)}.
     74      */
     75     public static final class MediaLibrarySession extends MediaSession2 {
     76         /**
     77          * Callback for the {@link MediaLibrarySession}.
     78          */
     79         public static class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
     80             /**
     81              * Called to get the root information for browsing by a particular client.
     82              * <p>
     83              * The implementation should verify that the client package has permission
     84              * to access browse media information before returning the root id; it
     85              * should return null if the client is not allowed to access this
     86              * information.
     87              * <p>
     88              * Note: this callback may be called on the main thread, regardless of the callback
     89              * executor.
     90              *
     91              * @param session the session for this event
     92              * @param controllerInfo information of the controller requesting access to browse
     93              *                       media.
     94              * @param extras An optional bundle of service-specific arguments to send
     95              * to the media library service when connecting and retrieving the
     96              * root id for browsing, or null if none. The contents of this
     97              * bundle may affect the information returned when browsing.
     98              * @return The {@link LibraryRoot} for accessing this app's content or null.
     99              * @see LibraryRoot#EXTRA_RECENT
    100              * @see LibraryRoot#EXTRA_OFFLINE
    101              * @see LibraryRoot#EXTRA_SUGGESTED
    102              * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT
    103              */
    104             public @Nullable LibraryRoot onGetLibraryRoot(@NonNull MediaLibrarySession session,
    105                     @NonNull ControllerInfo controllerInfo, @Nullable Bundle extras) {
    106                 return null;
    107             }
    108 
    109             /**
    110              * Called to get an item. Return result here for the browser.
    111              * <p>
    112              * Return {@code null} for no result or error.
    113              *
    114              * @param session the session for this event
    115              * @param mediaId item id to get media item.
    116              * @return a media item. {@code null} for no result or error.
    117              * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_ITEM
    118              */
    119             public @Nullable MediaItem2 onGetItem(@NonNull MediaLibrarySession session,
    120                     @NonNull ControllerInfo controllerInfo, @NonNull String mediaId) {
    121                 return null;
    122             }
    123 
    124             /**
    125              * Called to get children of given parent id. Return the children here for the browser.
    126              * <p>
    127              * Return an empty list for no children, and return {@code null} for the error.
    128              *
    129              * @param session the session for this event
    130              * @param parentId parent id to get children
    131              * @param page number of page
    132              * @param pageSize size of the page
    133              * @param extras extra bundle
    134              * @return list of children. Can be {@code null}.
    135              * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_CHILDREN
    136              */
    137             public @Nullable List<MediaItem2> onGetChildren(@NonNull MediaLibrarySession session,
    138                     @NonNull ControllerInfo controller, @NonNull String parentId, int page,
    139                     int pageSize, @Nullable Bundle extras) {
    140                 return null;
    141             }
    142 
    143             /**
    144              * Called when a controller subscribes to the parent.
    145              * <p>
    146              * It's your responsibility to keep subscriptions by your own and call
    147              * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)}
    148              * when the parent is changed.
    149              *
    150              * @param session the session for this event
    151              * @param controller controller
    152              * @param parentId parent id
    153              * @param extras extra bundle
    154              * @see SessionCommand2#COMMAND_CODE_LIBRARY_SUBSCRIBE
    155              */
    156             public void onSubscribe(@NonNull MediaLibrarySession session,
    157                     @NonNull ControllerInfo controller, @NonNull String parentId,
    158                     @Nullable Bundle extras) {
    159             }
    160 
    161             /**
    162              * Called when a controller unsubscribes to the parent.
    163              *
    164              * @param session the session for this event
    165              * @param controller controller
    166              * @param parentId parent id
    167              * @see SessionCommand2#COMMAND_CODE_LIBRARY_UNSUBSCRIBE
    168              */
    169             public void onUnsubscribe(@NonNull MediaLibrarySession session,
    170                     @NonNull ControllerInfo controller, @NonNull String parentId) {
    171             }
    172 
    173             /**
    174              * Called when a controller requests search.
    175              *
    176              * @param session the session for this event
    177              * @param query The search query sent from the media browser. It contains keywords
    178              *              separated by space.
    179              * @param extras The bundle of service-specific arguments sent from the media browser.
    180              * @see SessionCommand2#COMMAND_CODE_LIBRARY_SEARCH
    181              */
    182             public void onSearch(@NonNull MediaLibrarySession session,
    183                     @NonNull ControllerInfo controllerInfo, @NonNull String query,
    184                     @Nullable Bundle extras) {
    185             }
    186 
    187             /**
    188              * Called to get the search result. Return search result here for the browser which has
    189              * requested search previously.
    190              * <p>
    191              * Return an empty list for no search result, and return {@code null} for the error.
    192              *
    193              * @param session the session for this event
    194              * @param controllerInfo Information of the controller requesting the search result.
    195              * @param query The search query which was previously sent through
    196              *              {@link #onSearch(MediaLibrarySession, ControllerInfo, String, Bundle)}.
    197              * @param page page number. Starts from {@code 1}.
    198              * @param pageSize page size. Should be greater or equal to {@code 1}.
    199              * @param extras The bundle of service-specific arguments sent from the media browser.
    200              * @return search result. {@code null} for error.
    201              * @see SessionCommand2#COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT
    202              */
    203             public @Nullable List<MediaItem2> onGetSearchResult(
    204                     @NonNull MediaLibrarySession session, @NonNull ControllerInfo controllerInfo,
    205                     @NonNull String query, int page, int pageSize, @Nullable Bundle extras) {
    206                 return null;
    207             }
    208         }
    209 
    210         /**
    211          * Builder for {@link MediaLibrarySession}.
    212          */
    213         // Override all methods just to show them with the type instead of generics in Javadoc.
    214         // This workarounds javadoc issue described in the MediaSession2.BuilderBase.
    215         // Note: Don't override #setSessionCallback() because the callback can be set by the
    216         // constructor.
    217         public static final class Builder extends MediaSession2.BuilderBase<MediaLibrarySession,
    218                 Builder, MediaLibrarySessionCallback> {
    219             private MediaLibrarySessionImplBase.Builder mImpl;
    220 
    221             // Builder requires MediaLibraryService2 instead of Context just to ensure that the
    222             // builder can be only instantiated within the MediaLibraryService2.
    223             // Ideally it's better to make it inner class of service to enforce, it violates API
    224             // guideline that Builders should be the inner class of the building target.
    225             public Builder(@NonNull MediaLibraryService2 service,
    226                     @NonNull Executor callbackExecutor,
    227                     @NonNull MediaLibrarySessionCallback callback) {
    228                 super(service);
    229                 mImpl = new MediaLibrarySessionImplBase.Builder(service);
    230                 setImpl(mImpl);
    231                 setSessionCallback(callbackExecutor, callback);
    232             }
    233 
    234             @Override
    235             public @NonNull Builder setPlayer(@NonNull MediaPlayerInterface player) {
    236                 return super.setPlayer(player);
    237             }
    238 
    239             @Override
    240             public @NonNull Builder setPlaylistAgent(@NonNull MediaPlaylistAgent playlistAgent) {
    241                 return super.setPlaylistAgent(playlistAgent);
    242             }
    243 
    244             @Override
    245             public @NonNull Builder setVolumeProvider(
    246                     @Nullable VolumeProviderCompat volumeProvider) {
    247                 return super.setVolumeProvider(volumeProvider);
    248             }
    249 
    250             @Override
    251             public @NonNull Builder setSessionActivity(@Nullable PendingIntent pi) {
    252                 return super.setSessionActivity(pi);
    253             }
    254 
    255             @Override
    256             public @NonNull Builder setId(@NonNull String id) {
    257                 return super.setId(id);
    258             }
    259 
    260             @Override
    261             public @NonNull MediaLibrarySession build() {
    262                 return super.build();
    263             }
    264         }
    265 
    266         MediaLibrarySession(SupportLibraryImpl impl) {
    267             super(impl);
    268         }
    269 
    270         /**
    271          * Notify the controller of the change in a parent's children.
    272          * <p>
    273          * If the controller hasn't subscribed to the parent, the API will do nothing.
    274          * <p>
    275          * Controllers will use {@link MediaBrowser2#getChildren(String, int, int, Bundle)} to get
    276          * the list of children.
    277          *
    278          * @param controller controller to notify
    279          * @param parentId parent id with changes in its children
    280          * @param itemCount number of children.
    281          * @param extras extra information from session to controller
    282          */
    283         public void notifyChildrenChanged(@NonNull ControllerInfo controller,
    284                 @NonNull String parentId, int itemCount, @Nullable Bundle extras) {
    285             List<MediaSessionManager.RemoteUserInfo> subscribingBrowsers =
    286                     getServiceCompat().getSubscribingBrowsers(parentId);
    287             getImpl().notifyChildrenChanged(controller, parentId, itemCount, extras,
    288                     subscribingBrowsers);
    289         }
    290 
    291         /**
    292          * Notify all controllers that subscribed to the parent about change in the parent's
    293          * children, regardless of the extra bundle supplied by
    294          * {@link MediaBrowser2#subscribe(String, Bundle)}.
    295          *
    296          * @param parentId parent id
    297          * @param itemCount number of children
    298          * @param extras extra information from session to controller
    299          */
    300         // This is for the backward compatibility.
    301         public void notifyChildrenChanged(@NonNull String parentId, int itemCount,
    302                 @Nullable Bundle extras) {
    303             if (extras == null) {
    304                 getServiceCompat().notifyChildrenChanged(parentId);
    305             } else {
    306                 getServiceCompat().notifyChildrenChanged(parentId, extras);
    307             }
    308         }
    309 
    310         /**
    311          * Notify controller about change in the search result.
    312          *
    313          * @param controller controller to notify
    314          * @param query previously sent search query from the controller.
    315          * @param itemCount the number of items that have been found in the search.
    316          * @param extras extra bundle
    317          */
    318         public void notifySearchResultChanged(@NonNull ControllerInfo controller,
    319                 @NonNull String query, int itemCount, @Nullable Bundle extras) {
    320             getImpl().notifySearchResultChanged(controller, query, itemCount, extras);
    321         }
    322 
    323         private MediaLibraryService2 getService() {
    324             return (MediaLibraryService2) getContext();
    325         }
    326 
    327         private MediaBrowserServiceCompat getServiceCompat() {
    328             return getService().getServiceCompat();
    329         }
    330 
    331         @Override
    332         MediaLibrarySessionCallback getCallback() {
    333             return (MediaLibrarySessionCallback) super.getCallback();
    334         }
    335     }
    336 
    337     @Override
    338     MediaBrowserServiceCompat createBrowserServiceCompat() {
    339         return new MyBrowserService();
    340     }
    341 
    342     @Override
    343     int getSessionType() {
    344         return SessionToken2.TYPE_LIBRARY_SERVICE;
    345     }
    346 
    347     @Override
    348     public void onCreate() {
    349         super.onCreate();
    350 
    351         MediaSession2 session = getSession();
    352         if (!(session instanceof MediaLibrarySession)) {
    353             throw new RuntimeException("Expected MediaLibrarySession, but returned MediaSession2");
    354         }
    355     }
    356 
    357     private MediaLibrarySession getLibrarySession() {
    358         return (MediaLibrarySession) getSession();
    359     }
    360 
    361     @Override
    362     public IBinder onBind(Intent intent) {
    363         return super.onBind(intent);
    364     }
    365 
    366     /**
    367      * Called when another app requested to start this service.
    368      * <p>
    369      * Library service will accept or reject the connection with the
    370      * {@link MediaLibrarySessionCallback} in the created session.
    371      * <p>
    372      * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
    373      * expected ID that you've specified through the AndroidManifest.xml.
    374      * <p>
    375      * This method will be called on the main thread.
    376      *
    377      * @param sessionId session id written in the AndroidManifest.xml.
    378      * @return a new library session
    379      * @see Builder
    380      * @see #getSession()
    381      * @throws RuntimeException if returned session is invalid
    382      */
    383     @Override
    384     public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
    385 
    386     /**
    387      * Contains information that the library service needs to send to the client when
    388      * {@link MediaBrowser2#getLibraryRoot(Bundle)} is called.
    389      */
    390     public static final class LibraryRoot {
    391         /**
    392          * The lookup key for a boolean that indicates whether the library service should return a
    393          * librar root for recently played media items.
    394          *
    395          * <p>When creating a media browser for a given media library service, this key can be
    396          * supplied as a root hint for retrieving media items that are recently played.
    397          * If the media library service can provide such media items, the implementation must return
    398          * the key in the root hint when
    399          * {@link MediaLibrarySessionCallback#onGetLibraryRoot}
    400          * is called back.
    401          *
    402          * <p>The root hint may contain multiple keys.
    403          *
    404          * @see #EXTRA_OFFLINE
    405          * @see #EXTRA_SUGGESTED
    406          */
    407         public static final String EXTRA_RECENT = "android.media.extra.RECENT";
    408 
    409         /**
    410          * The lookup key for a boolean that indicates whether the library service should return a
    411          * library root for offline media items.
    412          *
    413          * <p>When creating a media browser for a given media library service, this key can be
    414          * supplied as a root hint for retrieving media items that are can be played without an
    415          * internet connection.
    416          * If the media library service can provide such media items, the implementation must return
    417          * the key in the root hint when
    418          * {@link MediaLibrarySessionCallback#onGetLibraryRoot}
    419          * is called back.
    420          *
    421          * <p>The root hint may contain multiple keys.
    422          *
    423          * @see #EXTRA_RECENT
    424          * @see #EXTRA_SUGGESTED
    425          */
    426         public static final String EXTRA_OFFLINE = "android.media.extra.OFFLINE";
    427 
    428         /**
    429          * The lookup key for a boolean that indicates whether the library service should return a
    430          * library root for suggested media items.
    431          *
    432          * <p>When creating a media browser for a given media library service, this key can be
    433          * supplied as a root hint for retrieving the media items suggested by the media library
    434          * service. The list of media items is considered ordered by relevance, first being the top
    435          * suggestion.
    436          * If the media library service can provide such media items, the implementation must return
    437          * the key in the root hint when
    438          * {@link MediaLibrarySessionCallback#onGetLibraryRoot}
    439          * is called back.
    440          *
    441          * <p>The root hint may contain multiple keys.
    442          *
    443          * @see #EXTRA_RECENT
    444          * @see #EXTRA_OFFLINE
    445          */
    446         public static final String EXTRA_SUGGESTED = "android.media.extra.SUGGESTED";
    447 
    448         private final String mRootId;
    449         private final Bundle mExtras;
    450 
    451         //private final LibraryRootProvider mProvider;
    452 
    453         /**
    454          * Constructs a library root.
    455          * @param rootId The root id for browsing.
    456          * @param extras Any extras about the library service.
    457          */
    458         public LibraryRoot(@NonNull String rootId, @Nullable Bundle extras) {
    459             if (rootId == null) {
    460                 throw new IllegalArgumentException("rootId shouldn't be null");
    461             }
    462             mRootId = rootId;
    463             mExtras = extras;
    464         }
    465 
    466         /**
    467          * Gets the root id for browsing.
    468          */
    469         public String getRootId() {
    470             return mRootId;
    471         }
    472 
    473         /**
    474          * Gets any extras about the library service.
    475          */
    476         public Bundle getExtras() {
    477             return mExtras;
    478         }
    479     }
    480 
    481     private class MyBrowserService extends MediaBrowserServiceCompat {
    482         @Override
    483         public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    484                 final Bundle extras) {
    485             if (MediaUtils2.isDefaultLibraryRootHint(extras)) {
    486                 // For connection request from the MediaController2. accept the connection from
    487                 // here, and let MediaLibrarySession decide whether to accept or reject the
    488                 // controller.
    489                 return sDefaultBrowserRoot;
    490             }
    491             final ControllerInfo controller = getController();
    492             MediaLibrarySession session = getLibrarySession();
    493             // Call onGetLibraryRoot() directly instead of execute on the executor. Here's the
    494             // reason.
    495             // We need to return browser root here. So if we run the callback on the executor, we
    496             // should wait for the completion.
    497             // However, we cannot wait if the callback executor is the main executor, which posts
    498             // the runnable to the main thread's. In that case, since this onGetRoot() always runs
    499             // on the main thread, the posted runnable for calling onGetLibraryRoot() wouldn't run
    500             // in here. Even worse, we cannot know whether it would be run on the main thread or
    501             // not.
    502             // Because of the reason, just call onGetLibraryRoot directly here. onGetLibraryRoot()
    503             // has documentation that it may be called on the main thread.
    504             LibraryRoot libraryRoot = session.getCallback().onGetLibraryRoot(
    505                     session, controller, extras);
    506             if (libraryRoot == null) {
    507                 return null;
    508             }
    509             return new BrowserRoot(libraryRoot.getRootId(), libraryRoot.getExtras());
    510         }
    511 
    512         @Override
    513         public void onLoadChildren(String parentId, Result<List<MediaItem>> result) {
    514             onLoadChildren(parentId, result, null);
    515         }
    516 
    517         @Override
    518         public void onLoadChildren(final String parentId, final Result<List<MediaItem>> result,
    519                 final Bundle options) {
    520             result.detach();
    521             final ControllerInfo controller = getController();
    522             getLibrarySession().getCallbackExecutor().execute(new Runnable() {
    523                 @Override
    524                 public void run() {
    525                     if (options != null) {
    526                         options.setClassLoader(MediaLibraryService2.this.getClassLoader());
    527                         try {
    528                             int page = options.getInt(EXTRA_PAGE);
    529                             int pageSize = options.getInt(EXTRA_PAGE_SIZE);
    530                             if (page > 0 && pageSize > 0) {
    531                                 // Requesting the list of children through pagination.
    532                                 List<MediaItem2> children = getLibrarySession().getCallback()
    533                                         .onGetChildren(getLibrarySession(), controller, parentId,
    534                                                 page, pageSize, options);
    535                                 result.sendResult(MediaUtils2.fromMediaItem2List(children));
    536                                 return;
    537                             } else if (options.containsKey(
    538                                     MediaBrowser2.MEDIA_BROWSER2_SUBSCRIBE)) {
    539                                 // This onLoadChildren() was triggered by MediaBrowser2.subscribe().
    540                                 options.remove(MediaBrowser2.MEDIA_BROWSER2_SUBSCRIBE);
    541                                 getLibrarySession().getCallback().onSubscribe(getLibrarySession(),
    542                                         controller, parentId, options.getBundle(ARGUMENT_EXTRAS));
    543                                 return;
    544                             }
    545                         } catch (BadParcelableException e) {
    546                             // pass-through.
    547                         }
    548                     }
    549                     List<MediaItem2> children = getLibrarySession().getCallback()
    550                             .onGetChildren(getLibrarySession(), controller, parentId,
    551                                     1 /* page */, Integer.MAX_VALUE /* pageSize*/,
    552                                     null /* extras */);
    553                     result.sendResult(MediaUtils2.fromMediaItem2List(children));
    554                 }
    555             });
    556         }
    557 
    558         @Override
    559         public void onLoadItem(final String itemId, final Result<MediaItem> result) {
    560             result.detach();
    561             final ControllerInfo controller = getController();
    562             getLibrarySession().getCallbackExecutor().execute(new Runnable() {
    563                 @Override
    564                 public void run() {
    565                     MediaItem2 item = getLibrarySession().getCallback().onGetItem(
    566                             getLibrarySession(), controller, itemId);
    567                     if (item == null) {
    568                         result.sendResult(null);
    569                     } else {
    570                         result.sendResult(MediaUtils2.createMediaItem(item));
    571                     }
    572                 }
    573             });
    574         }
    575 
    576         @Override
    577         public void onSearch(final String query, final Bundle extras,
    578                 final Result<List<MediaItem>> result) {
    579             result.detach();
    580             final ControllerInfo controller = getController();
    581             extras.setClassLoader(MediaLibraryService2.this.getClassLoader());
    582             try {
    583                 final int page = extras.getInt(ARGUMENT_PAGE);
    584                 final int pageSize = extras.getInt(ARGUMENT_PAGE_SIZE);
    585                 if (!(page > 0 && pageSize > 0)) {
    586                     getLibrarySession().getCallbackExecutor().execute(new Runnable() {
    587                         @Override
    588                         public void run() {
    589                             getLibrarySession().getCallback().onSearch(
    590                                     getLibrarySession(), controller, query, extras);
    591                         }
    592                     });
    593                 } else {
    594                     getLibrarySession().getCallbackExecutor().execute(new Runnable() {
    595                         @Override
    596                         public void run() {
    597                             List<MediaItem2> searchResult = getLibrarySession().getCallback()
    598                                     .onGetSearchResult(getLibrarySession(), controller, query,
    599                                             page, pageSize, extras);
    600                             if (searchResult == null) {
    601                                 result.sendResult(null);
    602                                 return;
    603                             }
    604                             result.sendResult(MediaUtils2.fromMediaItem2List(searchResult));
    605                         }
    606                     });
    607                 }
    608             } catch (BadParcelableException e) {
    609                 // Do nothing.
    610             }
    611         }
    612 
    613         @Override
    614         public void onCustomAction(String action, Bundle extras, Result<Bundle> result) {
    615             // No-op. Library session will handle the custom action.
    616         }
    617 
    618         private ControllerInfo getController() {
    619             MediaLibrarySession session = getLibrarySession();
    620             List<ControllerInfo> controllers = session.getConnectedControllers();
    621 
    622             MediaSessionManager.RemoteUserInfo info = getCurrentBrowserInfo();
    623             if (info == null) {
    624                 return null;
    625             }
    626 
    627             for (int i = 0; i < controllers.size(); i++) {
    628                 // Note: This cannot pick the right controller between two controllers in same
    629                 // process.
    630                 ControllerInfo controller = controllers.get(i);
    631                 if (controller.getPackageName().equals(info.getPackageName())
    632                         && controller.getUid() == info.getUid()) {
    633                     return controller;
    634                 }
    635             }
    636             return null;
    637         }
    638     }
    639 }
    640