Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c65429d7 authored by Kyunglyul Hyun's avatar Kyunglyul Hyun Committed by Android (Google) Code Review
Browse files

Merge "Media: Add ClientRecord in MediaRouter2Service"

parents 959d9c4b dafac54a
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -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())
+194 −67
Original line number Diff line number Diff line
@@ -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);
                }
            }
        }
    }
}
+8 −6
Original line number Diff line number Diff line
@@ -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() {