ConnectedControllersManager.java
/*
* 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 androidx.media2;
import android.util.Log;
import androidx.annotation.GuardedBy;
import androidx.collection.ArrayMap;
import androidx.media.MediaSessionManager.RemoteUserInfo;
import androidx.media2.MediaSession2.ControllerInfo;
import androidx.media2.MediaSession2.MediaSession2Impl;
import java.util.ArrayList;
import java.util.List;
/**
* Manages connected {@link ControllerInfo}. This is thread-safe.
*/
class ConnectedControllersManager<T> {
private static final String TAG = "MS2ControllerMgr";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Object mLock = new Object();
@GuardedBy("mLock")
private final ArrayMap<ControllerInfo, SessionCommandGroup2> mAllowedCommandGroupMap =
new ArrayMap<>();
@GuardedBy("mLock")
private final ArrayMap<T, ControllerInfo> mControllers = new ArrayMap<>();
@GuardedBy("mLock")
private final ArrayMap<ControllerInfo, T> mKeys = new ArrayMap<>();
@SuppressWarnings("WeakerAccess") /* synthetic access */
final MediaSession2Impl mSessionImpl;
ConnectedControllersManager(MediaSession2Impl session) {
mSessionImpl = session;
}
public void addController(T key, ControllerInfo controller, SessionCommandGroup2 commands) {
if (key == null || controller == null) {
if (DEBUG) {
throw new IllegalArgumentException("key nor controller shouldn't be null");
}
return;
}
synchronized (mLock) {
mAllowedCommandGroupMap.put(controller, commands);
mControllers.put(key, controller);
mKeys.put(controller, key);
}
// TODO: Also notify controller connected.
}
public void updateAllowedCommands(ControllerInfo controller, SessionCommandGroup2 commands) {
synchronized (mLock) {
if (!mAllowedCommandGroupMap.containsKey(controller)) {
if (DEBUG) {
Log.d(TAG, "Cannot update allowed command for disconnected controller "
+ controller);
}
return;
}
mAllowedCommandGroupMap.put(controller, commands);
}
// TODO: Also notify allowed command changes here.
}
public void removeController(T key) {
if (key == null) {
return;
}
final ControllerInfo controller;
synchronized (mLock) {
controller = mControllers.remove(key);
mKeys.remove(controller);
mAllowedCommandGroupMap.remove(controller);
}
notifyDisconnected(controller);
}
public void removeController(ControllerInfo controller) {
if (controller == null) {
return;
}
synchronized (mLock) {
T key = mKeys.remove(controller);
mControllers.remove(key);
mAllowedCommandGroupMap.remove(controller);
}
notifyDisconnected(controller);
}
private void notifyDisconnected(final ControllerInfo controller) {
if (DEBUG) {
Log.d(TAG, "Controller " + controller + " is disconnected");
}
if (mSessionImpl.isClosed() || controller == null) {
return;
}
mSessionImpl.getCallbackExecutor().execute(new Runnable() {
@Override
public void run() {
if (mSessionImpl.isClosed()) {
return;
}
mSessionImpl.getCallback().onDisconnected(mSessionImpl.getInstance(),
controller);
}
});
}
public List<ControllerInfo> getConnectedControllers() {
ArrayList<ControllerInfo> controllers = new ArrayList<>();
synchronized (mLock) {
for (int i = 0; i < mControllers.size(); i++) {
controllers.add(mControllers.valueAt(i));
}
}
return controllers;
}
public boolean isConnected(ControllerInfo controller) {
synchronized (mLock) {
return mKeys.get(controller) != null;
}
}
public boolean isAllowedCommand(ControllerInfo controller, SessionCommand2 command) {
SessionCommandGroup2 allowedCommands;
synchronized (mLock) {
allowedCommands = mAllowedCommandGroupMap.get(controller);
}
return allowedCommands != null && allowedCommands.hasCommand(command);
}
public boolean isAllowedCommand(ControllerInfo controller, int commandCode) {
SessionCommandGroup2 allowedCommands;
synchronized (mLock) {
allowedCommands = mAllowedCommandGroupMap.get(controller);
}
return allowedCommands != null && allowedCommands.hasCommand(commandCode);
}
public ControllerInfo getController(T key) {
if (key == null) {
return null;
}
synchronized (mLock) {
if (key instanceof RemoteUserInfo) {
// Workaround MediaBrowserServiceCompat's issue that that RemoteUserInfo from
// onGetRoot and other methods are differ even for the same controller.
// TODO: Remove this workaround
RemoteUserInfo user = (RemoteUserInfo) key;
for (int i = 0; i < mControllers.size(); i++) {
RemoteUserInfo info = (RemoteUserInfo) mControllers.keyAt(i);
if (user.getPackageName().equals(info.getPackageName())
&& user.getUid() == info.getUid()) {
return mControllers.valueAt(i);
}
}
return null;
}
return mControllers.get(key);
}
}
}