Media: Add ClientRecord in MediaRouter2Service
ClientRecord is a record for media router client. Save the client info on MediaRouter2Service to make setControlCategories work and MediaRouter2Manager get the callback onControlCategoriesChanged. Test: atest mediaroutertest (after atest mediarouteprovider) Change-Id: Iaf79242e18a699e339e91c8b0baceb767dc36bd4
This commit is contained in:
@@ -18,8 +18,6 @@ package com.android.mediaroutertest;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.after;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -100,8 +98,8 @@ public class MediaRouterManagerTest {
|
||||
MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
|
||||
mManager.addCallback(mExecutor, mockCallback);
|
||||
|
||||
verify(mockCallback, after(AWAIT_MS).never()).onControlCategoriesChanged(eq(uid),
|
||||
any(List.class));
|
||||
verify(mockCallback, after(AWAIT_MS).never()).onControlCategoriesChanged(uid,
|
||||
TEST_CONTROL_CATEGORIES);
|
||||
|
||||
mRouter.setControlCategories(TEST_CONTROL_CATEGORIES);
|
||||
verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.android.server.media;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.IMediaRouter2Manager;
|
||||
import android.media.IMediaRouterClient;
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
@@ -33,10 +36,14 @@ import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* TODO: Merge this to MediaRouterService once it's finished.
|
||||
@@ -47,20 +54,57 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
private final Context mContext;
|
||||
private final Object mLock = new Object();
|
||||
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<IBinder, ClientRecord> mAllClientRecords = new ArrayMap<>();
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>();
|
||||
@GuardedBy("mLock")
|
||||
private int mCurrentUserId = -1;
|
||||
|
||||
MediaRouter2ServiceImpl(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void registerManagerAsUser(IMediaRouter2Manager client,
|
||||
String packageName, int userId) {
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("client must not be null");
|
||||
public void registerClientAsUser(@NonNull IMediaRouterClient client,
|
||||
@NonNull String packageName, int userId) {
|
||||
Objects.requireNonNull(client, "client must not be null");
|
||||
|
||||
final int uid = Binder.getCallingUid();
|
||||
final int pid = Binder.getCallingPid();
|
||||
final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
|
||||
false /*allowAll*/, true /*requireFull*/, "registerClientAsUser", packageName);
|
||||
final boolean trusted = mContext.checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
registerClientLocked(client, uid, pid, packageName, resolvedUserId, trusted);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterClient(@NonNull IMediaRouterClient client) {
|
||||
Objects.requireNonNull(client, "client must not be null");
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
unregisterClientLocked(client, false);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerManagerAsUser(@NonNull IMediaRouter2Manager manager,
|
||||
@NonNull String packageName, int userId) {
|
||||
Objects.requireNonNull(manager, "manager must not be null");
|
||||
//TODO: should check permission
|
||||
final boolean trusted = true;
|
||||
|
||||
@@ -71,32 +115,30 @@ class MediaRouter2ServiceImpl {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
registerManagerLocked(client, uid, pid, packageName, resolvedUserId, trusted);
|
||||
registerManagerLocked(manager, uid, pid, packageName, resolvedUserId, trusted);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterManager(IMediaRouter2Manager client) {
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("client must not be null");
|
||||
}
|
||||
public void unregisterManager(@NonNull IMediaRouter2Manager manager) {
|
||||
Objects.requireNonNull(manager, "manager must not be null");
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
unregisterManagerLocked(client, false);
|
||||
unregisterManagerLocked(manager, false);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
public void setControlCategories(IMediaRouterClient client, List<String> categories) {
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("client must not be null");
|
||||
}
|
||||
public void setControlCategories(@NonNull IMediaRouterClient client,
|
||||
@Nullable List<String> categories) {
|
||||
Objects.requireNonNull(client, "client must not be null");
|
||||
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
@@ -107,12 +149,12 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public void setRemoteRoute(IMediaRouter2Manager client,
|
||||
int uid, String routeId, boolean explicit) {
|
||||
public void setRemoteRoute(@NonNull IMediaRouter2Manager manager,
|
||||
int uid, @Nullable String routeId, boolean explicit) {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
setRemoteRouteLocked(client, uid, routeId, explicit);
|
||||
setRemoteRouteLocked(manager, uid, routeId, explicit);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
@@ -141,15 +183,60 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
void clientDied(ManagerRecord managerRecord) {
|
||||
void clientDied(ClientRecord clientRecord) {
|
||||
synchronized (mLock) {
|
||||
unregisterManagerLocked(managerRecord.mClient, true);
|
||||
unregisterClientLocked(clientRecord.mClient, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerManagerLocked(IMediaRouter2Manager client,
|
||||
void managerDied(ManagerRecord managerRecord) {
|
||||
synchronized (mLock) {
|
||||
unregisterManagerLocked(managerRecord.mManager, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerClientLocked(IMediaRouterClient client,
|
||||
int uid, int pid, String packageName, int userId, boolean trusted) {
|
||||
final IBinder binder = client.asBinder();
|
||||
ClientRecord clientRecord = mAllClientRecords.get(binder);
|
||||
if (clientRecord == null) {
|
||||
boolean newUser = false;
|
||||
UserRecord userRecord = mUserRecords.get(userId);
|
||||
if (userRecord == null) {
|
||||
userRecord = new UserRecord(userId);
|
||||
newUser = true;
|
||||
}
|
||||
clientRecord = new ClientRecord(userRecord, client, uid, pid, packageName, trusted);
|
||||
try {
|
||||
binder.linkToDeath(clientRecord, 0);
|
||||
} catch (RemoteException ex) {
|
||||
throw new RuntimeException("Media router client died prematurely.", ex);
|
||||
}
|
||||
|
||||
if (newUser) {
|
||||
mUserRecords.put(userId, userRecord);
|
||||
initializeUserLocked(userRecord);
|
||||
}
|
||||
|
||||
userRecord.mClientRecords.add(clientRecord);
|
||||
mAllClientRecords.put(binder, clientRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterClientLocked(IMediaRouterClient client, boolean died) {
|
||||
ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
|
||||
if (clientRecord != null) {
|
||||
UserRecord userRecord = clientRecord.mUserRecord;
|
||||
userRecord.mClientRecords.remove(clientRecord);
|
||||
//TODO: update discovery request
|
||||
clientRecord.dispose();
|
||||
disposeUserIfNeededLocked(userRecord); // since client removed from user
|
||||
}
|
||||
}
|
||||
|
||||
private void registerManagerLocked(IMediaRouter2Manager manager,
|
||||
int uid, int pid, String packageName, int userId, boolean trusted) {
|
||||
final IBinder binder = manager.asBinder();
|
||||
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
|
||||
if (managerRecord == null) {
|
||||
boolean newUser = false;
|
||||
@@ -158,11 +245,11 @@ class MediaRouter2ServiceImpl {
|
||||
userRecord = new UserRecord(userId);
|
||||
newUser = true;
|
||||
}
|
||||
managerRecord = new ManagerRecord(userRecord, client, uid, pid, packageName, trusted);
|
||||
managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName, trusted);
|
||||
try {
|
||||
binder.linkToDeath(managerRecord, 0);
|
||||
} catch (RemoteException ex) {
|
||||
throw new RuntimeException("Media router client died prematurely.", ex);
|
||||
throw new RuntimeException("Media router manager died prematurely.", ex);
|
||||
}
|
||||
|
||||
if (newUser) {
|
||||
@@ -176,22 +263,29 @@ class MediaRouter2ServiceImpl {
|
||||
//TODO: remove this when it's unnecessary
|
||||
// Sends published routes to newly added manager
|
||||
userRecord.mHandler.scheduleUpdateManagerState();
|
||||
|
||||
final int count = userRecord.mClientRecords.size();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ClientRecord clientRecord = userRecord.mClientRecords.get(i);
|
||||
clientRecord.mUserRecord.mHandler.obtainMessage(
|
||||
UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterManagerLocked(IMediaRouter2Manager client, boolean died) {
|
||||
ManagerRecord clientRecord = mAllManagerRecords.remove(client.asBinder());
|
||||
if (clientRecord != null) {
|
||||
UserRecord userRecord = clientRecord.mUserRecord;
|
||||
userRecord.mManagerRecords.remove(clientRecord);
|
||||
clientRecord.dispose();
|
||||
disposeUserIfNeededLocked(userRecord); // since client removed from user
|
||||
private void unregisterManagerLocked(IMediaRouter2Manager manager, boolean died) {
|
||||
ManagerRecord managerRecord = mAllManagerRecords.remove(manager.asBinder());
|
||||
if (managerRecord != null) {
|
||||
UserRecord userRecord = managerRecord.mUserRecord;
|
||||
userRecord.mManagerRecords.remove(managerRecord);
|
||||
managerRecord.dispose();
|
||||
disposeUserIfNeededLocked(userRecord); // since manager removed from user
|
||||
}
|
||||
}
|
||||
|
||||
private void setRemoteRouteLocked(IMediaRouter2Manager client,
|
||||
private void setRemoteRouteLocked(IMediaRouter2Manager manager,
|
||||
int uid, String routeId, boolean explicit) {
|
||||
ManagerRecord managerRecord = mAllManagerRecords.get(client.asBinder());
|
||||
ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
|
||||
if (managerRecord != null) {
|
||||
if (explicit && managerRecord.mTrusted) {
|
||||
Pair<Integer, String> obj = new Pair<>(uid, routeId);
|
||||
@@ -202,15 +296,14 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
|
||||
private void setControlCategoriesLocked(IMediaRouterClient client, List<String> categories) {
|
||||
//TODO: implement this when we have client record (MediaRouter2?)
|
||||
// final IBinder binder = client.asBinder();
|
||||
// ClientRecord clientRecord = mAllClientRecords.get(binder);
|
||||
//
|
||||
// if (clientRecord != null) {
|
||||
// clientRecord.mControlCategories = categories;
|
||||
// clientRecord.mUserRecord.mHandler.obtainMessage(
|
||||
// UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget();
|
||||
// }
|
||||
final IBinder binder = client.asBinder();
|
||||
ClientRecord clientRecord = mAllClientRecords.get(binder);
|
||||
|
||||
if (clientRecord != null) {
|
||||
clientRecord.mControlCategories = categories;
|
||||
clientRecord.mUserRecord.mHandler.obtainMessage(
|
||||
UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeUserLocked(UserRecord userRecord) {
|
||||
@@ -228,6 +321,7 @@ class MediaRouter2ServiceImpl {
|
||||
// then leave it alone since we might be connected to a route or want to query
|
||||
// the same route information again soon.
|
||||
if (userRecord.mUserId != mCurrentUserId
|
||||
&& userRecord.mClientRecords.isEmpty()
|
||||
&& userRecord.mManagerRecords.isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, userRecord + ": Disposed");
|
||||
@@ -239,6 +333,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
final class UserRecord {
|
||||
public final int mUserId;
|
||||
final ArrayList<ClientRecord> mClientRecords = new ArrayList<>();
|
||||
final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
|
||||
final UserHandler mHandler;
|
||||
|
||||
@@ -248,15 +343,16 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
final class ManagerRecord implements IBinder.DeathRecipient {
|
||||
final class ClientRecord implements IBinder.DeathRecipient {
|
||||
public final UserRecord mUserRecord;
|
||||
public final IMediaRouter2Manager mClient;
|
||||
public final IMediaRouterClient mClient;
|
||||
public final int mUid;
|
||||
public final int mPid;
|
||||
public final String mPackageName;
|
||||
public final boolean mTrusted;
|
||||
public List<String> mControlCategories;
|
||||
|
||||
ManagerRecord(UserRecord userRecord, IMediaRouter2Manager client,
|
||||
ClientRecord(UserRecord userRecord, IMediaRouterClient client,
|
||||
int uid, int pid, String packageName, boolean trusted) {
|
||||
mUserRecord = userRecord;
|
||||
mClient = client;
|
||||
@@ -264,6 +360,7 @@ class MediaRouter2ServiceImpl {
|
||||
mPid = pid;
|
||||
mPackageName = packageName;
|
||||
mTrusted = trusted;
|
||||
mControlCategories = Collections.emptyList();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
@@ -274,6 +371,34 @@ class MediaRouter2ServiceImpl {
|
||||
public void binderDied() {
|
||||
clientDied(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class ManagerRecord implements IBinder.DeathRecipient {
|
||||
public final UserRecord mUserRecord;
|
||||
public final IMediaRouter2Manager mManager;
|
||||
public final int mUid;
|
||||
public final int mPid;
|
||||
public final String mPackageName;
|
||||
public final boolean mTrusted;
|
||||
|
||||
ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager,
|
||||
int uid, int pid, String packageName, boolean trusted) {
|
||||
mUserRecord = userRecord;
|
||||
mManager = manager;
|
||||
mUid = uid;
|
||||
mPid = pid;
|
||||
mPackageName = packageName;
|
||||
mTrusted = trusted;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
mManager.asBinder().unlinkToDeath(this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
managerDied(this);
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + this);
|
||||
@@ -284,7 +409,7 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Client " + mPackageName + " (pid " + mPid + ")";
|
||||
return "Manager " + mPackageName + " (pid " + mPid + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +461,7 @@ class MediaRouter2ServiceImpl {
|
||||
break;
|
||||
}
|
||||
case MSG_UPDATE_CLIENT_USAGE: {
|
||||
updateClientUsage();
|
||||
updateClientUsage((ClientRecord) msg.obj);
|
||||
break;
|
||||
}
|
||||
case MSG_UPDATE_MANAGER_STATE: {
|
||||
@@ -424,7 +549,7 @@ class MediaRouter2ServiceImpl {
|
||||
synchronized (service.mLock) {
|
||||
final int count = mUserRecord.mManagerRecords.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
mTempManagers.add(mUserRecord.mManagerRecords.get(i).mClient);
|
||||
mTempManagers.add(mUserRecord.mManagerRecords.get(i).mManager);
|
||||
}
|
||||
}
|
||||
//TODO: Call !proper callbacks when provider descriptor is implemented.
|
||||
@@ -445,26 +570,28 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateClientUsage() {
|
||||
//TODO: merge these code to updateClientState()
|
||||
|
||||
// List<IMediaRouter2Manager> managers = new ArrayList<>();
|
||||
// synchronized (mService.mLock) {
|
||||
// final int count = mUserRecord.mManagerRecords.size();
|
||||
// for (int i = 0; i < count; i++) {
|
||||
// managers.add(mUserRecord.mManagerRecords.get(i).mClient);
|
||||
// }
|
||||
// }
|
||||
// final int count = managers.size();
|
||||
// for (int i = 0; i < count; i++) {
|
||||
// try {
|
||||
// managers.get(i).onControlCategoriesChanged(clientRecord.mUid,
|
||||
// clientRecord.mControlCategories);
|
||||
// } catch (RemoteException ex) {
|
||||
// Slog.w(TAG, "Failed to call onControlCategoriesChanged. "
|
||||
// + "Manager probably died.", ex);
|
||||
// }
|
||||
// }
|
||||
private void updateClientUsage(ClientRecord clientRecord) {
|
||||
MediaRouter2ServiceImpl service = mServiceRef.get();
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
List<IMediaRouter2Manager> managers = new ArrayList<>();
|
||||
synchronized (service.mLock) {
|
||||
final int count = mUserRecord.mManagerRecords.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
managers.add(mUserRecord.mManagerRecords.get(i).mManager);
|
||||
}
|
||||
}
|
||||
final int count = managers.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
try {
|
||||
managers.get(i).notifyControlCategoriesChanged(clientRecord.mUid,
|
||||
clientRecord.mControlCategories);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.w(TAG, "Failed to call onControlCategoriesChanged. "
|
||||
+ "Manager probably died.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +250,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
mService2.registerClientAsUser(client, packageName, userId);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@@ -267,6 +268,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
mService2.unregisterClient(client);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@@ -416,26 +418,26 @@ public final class MediaRouterService extends IMediaRouterService.Stub
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public void registerManagerAsUser(IMediaRouter2Manager client,
|
||||
public void registerManagerAsUser(IMediaRouter2Manager manager,
|
||||
String packageName, int userId) {
|
||||
final int uid = Binder.getCallingUid();
|
||||
if (!validatePackageName(uid, packageName)) {
|
||||
throw new SecurityException("packageName must match the calling uid");
|
||||
}
|
||||
mService2.registerManagerAsUser(client, packageName, userId);
|
||||
mService2.registerManagerAsUser(manager, packageName, userId);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public void unregisterManager(IMediaRouter2Manager client) {
|
||||
mService2.unregisterManager(client);
|
||||
public void unregisterManager(IMediaRouter2Manager manager) {
|
||||
mService2.unregisterManager(manager);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public void setRemoteRoute(IMediaRouter2Manager client,
|
||||
public void setRemoteRoute(IMediaRouter2Manager manager,
|
||||
int uid, String routeId, boolean explicit) {
|
||||
mService2.setRemoteRoute(client, uid, routeId, explicit);
|
||||
mService2.setRemoteRoute(manager, uid, routeId, explicit);
|
||||
}
|
||||
|
||||
void restoreBluetoothA2dp() {
|
||||
|
||||
Reference in New Issue
Block a user