• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.car.media.common.source;
18 
19 import static com.android.car.apps.common.util.CarAppsDebugUtils.idHash;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.support.v4.media.MediaBrowserCompat;
26 import android.util.Log;
27 
28 /**
29  * A helper class to connect to a single {@link MediaBrowserCompat}. Connecting to a new one
30  * automatically disconnects the previous browser. Changes of the currently connected browser are
31  * sent via {@link MediaBrowserConnector.Callback}.
32  */
33 
34 public class MediaBrowserConnector {
35 
36     private static final String TAG = "MediaBrowserConnector";
37 
38     /** The callback to receive the currently connected {@link MediaBrowserCompat}. */
39     public interface Callback {
40         /** When disconnecting, the given browser will be null. */
onConnectedBrowserChanged(@ullable MediaBrowserCompat browser)41         void onConnectedBrowserChanged(@Nullable MediaBrowserCompat browser);
42     }
43 
44     private final Context mContext;
45     private final Callback mCallback;
46 
47     @Nullable private ComponentName mBrowseService;
48     @Nullable private MediaBrowserCompat mBrowser;
49 
50     /**
51      * Create a new MediaBrowserConnector.
52      *
53      * @param context The Context with which to build MediaBrowsers.
54      */
MediaBrowserConnector(@onNull Context context, @NonNull Callback callback)55     MediaBrowserConnector(@NonNull Context context, @NonNull Callback callback) {
56         mContext = context;
57         mCallback = callback;
58     }
59 
60     /** Counter so callbacks from obsolete connections can be ignored. */
61     private int mBrowserConnectionCallbackCounter = 0;
62 
63     private class BrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
64 
65         private final int mSequenceNumber = ++mBrowserConnectionCallbackCounter;
66         private final String mCallbackPackage = mBrowseService.getPackageName();
67 
isValidCall(String method)68         private boolean isValidCall(String method) {
69             if (mSequenceNumber != mBrowserConnectionCallbackCounter) {
70                 Log.e(TAG, "Ignoring callback " + method + " for " + mCallbackPackage + " seq: "
71                         + mSequenceNumber + " current: " + mBrowserConnectionCallbackCounter
72                         + " current: " + mBrowseService.getPackageName());
73                 return false;
74             } else if (Log.isLoggable(TAG, Log.DEBUG)) {
75                 Log.d(TAG, method + " " + mBrowseService.getPackageName() + idHash(mBrowser));
76             }
77             return true;
78         }
79 
80         @Override
onConnected()81         public void onConnected() {
82             if (isValidCall("onConnected")) {
83                 mCallback.onConnectedBrowserChanged(mBrowser);
84             }
85         }
86 
87         @Override
onConnectionFailed()88         public void onConnectionFailed() {
89             if (isValidCall("onConnectionFailed")) {
90                 mCallback.onConnectedBrowserChanged(null);
91             }
92         }
93 
94         @Override
onConnectionSuspended()95         public void onConnectionSuspended() {
96             if (isValidCall("onConnectionSuspended")) {
97                 mCallback.onConnectedBrowserChanged(null);
98             }
99         }
100     }
101 
102     /**
103      * Creates and connects a new {@link MediaBrowserCompat} if the given {@link ComponentName}
104      * isn't null. If needed, the previous browser is disconnected.
105      * @param browseService the ComponentName of the media browser service.
106      * @see MediaBrowserCompat#MediaBrowserCompat(Context, ComponentName,
107      * MediaBrowserCompat.ConnectionCallback, android.os.Bundle)
108      */
connectTo(@ullable ComponentName browseService)109     public void connectTo(@Nullable ComponentName browseService) {
110         if (mBrowser != null && mBrowser.isConnected()) {
111             if (Log.isLoggable(TAG, Log.DEBUG)) {
112                 Log.d(TAG, "Disconnecting: " + mBrowseService.getPackageName() + idHash(mBrowser));
113             }
114             mCallback.onConnectedBrowserChanged(null);
115             mBrowser.disconnect();
116         }
117 
118         mBrowseService = browseService;
119         if (mBrowseService != null) {
120             mBrowser = createMediaBrowser(mBrowseService, new BrowserConnectionCallback());
121             if (Log.isLoggable(TAG, Log.DEBUG)) {
122                 Log.d(TAG, "Connecting to: " + mBrowseService.getPackageName() + idHash(mBrowser));
123             }
124             try {
125                 mBrowser.connect();
126             } catch (IllegalStateException ex) {
127                 // Is this comment still valid ?
128                 // Ignore: MediaBrowse could be in an intermediate state (not connected, but not
129                 // disconnected either.). In this situation, trying to connect again can throw
130                 // this exception, but there is no way to know without trying.
131                 Log.e(TAG, "Connection exception: " + ex);
132             }
133         } else {
134             mBrowser = null;
135         }
136     }
137 
138     // Override for testing.
139     @NonNull
createMediaBrowser(@onNull ComponentName browseService, @NonNull MediaBrowserCompat.ConnectionCallback callback)140     protected MediaBrowserCompat createMediaBrowser(@NonNull ComponentName browseService,
141             @NonNull MediaBrowserCompat.ConnectionCallback callback) {
142         return new MediaBrowserCompat(mContext, browseService, callback, null);
143     }
144 }
145