blob: 36f90c85bec150dfd5792ee375d05ee368336115 [file] [log] [blame]
/*
* Copyright 2018 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 com.android.car.media.common.source;
import static com.android.car.apps.common.util.CarAppsDebugUtils.idHash;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.support.v4.media.MediaBrowserCompat;
import android.util.Log;
/**
* A helper class to connect to a single {@link MediaBrowserCompat}. Connecting to a new one
* automatically disconnects the previous browser. Changes of the currently connected browser are
* sent via {@link MediaBrowserConnector.Callback}.
*/
public class MediaBrowserConnector {
private static final String TAG = "MediaBrowserConnector";
/** The callback to receive the currently connected {@link MediaBrowserCompat}. */
public interface Callback {
/** When disconnecting, the given browser will be null. */
void onConnectedBrowserChanged(@Nullable MediaBrowserCompat browser);
}
private final Context mContext;
private final Callback mCallback;
@Nullable private ComponentName mBrowseService;
@Nullable private MediaBrowserCompat mBrowser;
/**
* Create a new MediaBrowserConnector.
*
* @param context The Context with which to build MediaBrowsers.
*/
MediaBrowserConnector(@NonNull Context context, @NonNull Callback callback) {
mContext = context;
mCallback = callback;
}
/** Counter so callbacks from obsolete connections can be ignored. */
private int mBrowserConnectionCallbackCounter = 0;
private class BrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
private final int mSequenceNumber = ++mBrowserConnectionCallbackCounter;
private final String mCallbackPackage = mBrowseService.getPackageName();
private boolean isValidCall(String method) {
if (mSequenceNumber != mBrowserConnectionCallbackCounter) {
Log.e(TAG, "Ignoring callback " + method + " for " + mCallbackPackage + " seq: "
+ mSequenceNumber + " current: " + mBrowserConnectionCallbackCounter
+ " current: " + mBrowseService.getPackageName());
return false;
} else if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, method + " " + mBrowseService.getPackageName() + idHash(mBrowser));
}
return true;
}
@Override
public void onConnected() {
if (isValidCall("onConnected")) {
mCallback.onConnectedBrowserChanged(mBrowser);
}
}
@Override
public void onConnectionFailed() {
if (isValidCall("onConnectionFailed")) {
mCallback.onConnectedBrowserChanged(null);
}
}
@Override
public void onConnectionSuspended() {
if (isValidCall("onConnectionSuspended")) {
mCallback.onConnectedBrowserChanged(null);
}
}
}
/**
* Creates and connects a new {@link MediaBrowserCompat} if the given {@link ComponentName}
* isn't null. If needed, the previous browser is disconnected.
* @param browseService the ComponentName of the media browser service.
* @see MediaBrowserCompat#MediaBrowserCompat(Context, ComponentName,
* MediaBrowserCompat.ConnectionCallback, android.os.Bundle)
*/
public void connectTo(@Nullable ComponentName browseService) {
if (mBrowser != null && mBrowser.isConnected()) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Disconnecting: " + mBrowseService.getPackageName() + idHash(mBrowser));
}
mCallback.onConnectedBrowserChanged(null);
mBrowser.disconnect();
}
mBrowseService = browseService;
if (mBrowseService != null) {
mBrowser = createMediaBrowser(mBrowseService, new BrowserConnectionCallback());
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Connecting to: " + mBrowseService.getPackageName() + idHash(mBrowser));
}
try {
mBrowser.connect();
} catch (IllegalStateException ex) {
// Is this comment still valid ?
// Ignore: MediaBrowse could be in an intermediate state (not connected, but not
// disconnected either.). In this situation, trying to connect again can throw
// this exception, but there is no way to know without trying.
Log.e(TAG, "Connection exception: " + ex);
}
} else {
mBrowser = null;
}
}
// Override for testing.
@NonNull
protected MediaBrowserCompat createMediaBrowser(@NonNull ComponentName browseService,
@NonNull MediaBrowserCompat.ConnectionCallback callback) {
return new MediaBrowserCompat(mContext, browseService, callback, null);
}
}