Loading services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java 0 → 100644 +438 −0 Original line number Diff line number Diff line /* * Copyright 2019 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.server.media; import android.app.ActivityManager; import android.content.Context; import android.media.IMediaRouter2ManagerClient; import android.media.IMediaRouterClient; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * TODO: Merge this to MediaRouterService once it's finished. */ class MediaRouter2ServiceImpl { private static final String TAG = "MediaRouter2ServiceImpl"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final Context mContext; private final Object mLock = new Object(); private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>(); private int mCurrentUserId = -1; MediaRouter2ServiceImpl(Context context) { mContext = context; } public void registerManagerAsUser(IMediaRouter2ManagerClient client, String packageName, int userId) { if (client == null) { throw new IllegalArgumentException("client must not be null"); } //TODO: should check permission final boolean trusted = true; final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, false /*allowAll*/, true /*requireFull*/, "registerManagerAsUser", packageName); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { registerManagerLocked(client, uid, pid, packageName, resolvedUserId, trusted); } } finally { Binder.restoreCallingIdentity(token); } } public void unregisterManager(IMediaRouter2ManagerClient client) { if (client == null) { throw new IllegalArgumentException("client must not be null"); } final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { unregisterManagerLocked(client, false); } } finally { Binder.restoreCallingIdentity(token); } } public void setControlCategories(IMediaRouterClient client, List<String> categories) { if (client == null) { throw new IllegalArgumentException("client must not be null"); } final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { setControlCategoriesLocked(client, categories); } } finally { Binder.restoreCallingIdentity(token); } } public void setRemoteRoute(IMediaRouter2ManagerClient client, int uid, String routeId, boolean explicit) { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { setRemoteRouteLocked(client, uid, routeId, explicit); } } finally { Binder.restoreCallingIdentity(token); } } void clientDied(ManagerRecord managerRecord) { synchronized (mLock) { unregisterManagerLocked(managerRecord.mClient, true); } } private void registerManagerLocked(IMediaRouter2ManagerClient client, int uid, int pid, String packageName, int userId, boolean trusted) { final IBinder binder = client.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); if (managerRecord == null) { boolean newUser = false; UserRecord userRecord = mUserRecords.get(userId); if (userRecord == null) { userRecord = new UserRecord(userId); newUser = true; } managerRecord = new ManagerRecord(userRecord, client, uid, pid, packageName, trusted); try { binder.linkToDeath(managerRecord, 0); } catch (RemoteException ex) { throw new RuntimeException("Media router client died prematurely.", ex); } if (newUser) { mUserRecords.put(userId, userRecord); initializeUserLocked(userRecord); } userRecord.mManagerRecords.add(managerRecord); mAllManagerRecords.put(binder, managerRecord); //TODO: send client's info to manager } } private void unregisterManagerLocked(IMediaRouter2ManagerClient 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 setRemoteRouteLocked(IMediaRouter2ManagerClient client, int uid, String routeId, boolean explicit) { ManagerRecord managerRecord = mAllManagerRecords.get(client.asBinder()); if (managerRecord != null) { if (explicit && managerRecord.mTrusted) { Pair<Integer, String> obj = new Pair<>(uid, routeId); managerRecord.mUserRecord.mHandler.obtainMessage( UserHandler.MSG_SELECT_REMOTE_ROUTE, obj).sendToTarget(); } } } 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(); // } } private void initializeUserLocked(UserRecord userRecord) { if (DEBUG) { Slog.d(TAG, userRecord + ": Initialized"); } if (userRecord.mUserId == mCurrentUserId) { userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START); } } private void disposeUserIfNeededLocked(UserRecord userRecord) { // If there are no records left and the user is no longer current then go ahead // and purge the user record and all of its associated state. If the user is current // 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.mManagerRecords.isEmpty()) { if (DEBUG) { Slog.d(TAG, userRecord + ": Disposed"); } mUserRecords.remove(userRecord.mUserId); // Note: User already stopped (by switchUser) so no need to send stop message here. } } final class UserRecord { public final int mUserId; final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>(); final UserHandler mHandler; UserRecord(int userId) { mUserId = userId; mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this); } } final class ManagerRecord implements IBinder.DeathRecipient { public final UserRecord mUserRecord; public final IMediaRouter2ManagerClient mClient; public final int mUid; public final int mPid; public final String mPackageName; public final boolean mTrusted; ManagerRecord(UserRecord userRecord, IMediaRouter2ManagerClient client, int uid, int pid, String packageName, boolean trusted) { mUserRecord = userRecord; mClient = client; mUid = uid; mPid = pid; mPackageName = packageName; mTrusted = trusted; } public void dispose() { mClient.asBinder().unlinkToDeath(this, 0); } @Override public void binderDied() { clientDied(this); } public void dump(PrintWriter pw, String prefix) { pw.println(prefix + this); final String indent = prefix + " "; pw.println(indent + "mTrusted=" + mTrusted); } @Override public String toString() { return "Client " + mPackageName + " (pid " + mPid + ")"; } } static final class UserHandler extends Handler implements MediaRoute2ProviderWatcher.Callback, MediaRoute2ProviderProxy.Callback { //TODO: Should be rearranged public static final int MSG_START = 1; public static final int MSG_STOP = 2; private static final int MSG_UPDATE_CLIENT_STATE = 8; private static final int MSG_SELECT_REMOTE_ROUTE = 10; private static final int MSG_UPDATE_CLIENT_USAGE = 11; private final WeakReference<MediaRouter2ServiceImpl> mServiceRef; private final UserRecord mUserRecord; private final MediaRoute2ProviderWatcher mWatcher; private final ArrayList<IMediaRouter2ManagerClient> mTempManagers = new ArrayList<>(); private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders = new ArrayList<>(); private boolean mRunning; private boolean mClientStateUpdateScheduled; UserHandler(MediaRouter2ServiceImpl service, UserRecord userRecord) { mServiceRef = new WeakReference<>(service); mUserRecord = userRecord; mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, this, mUserRecord.mUserId); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_START: { start(); break; } case MSG_STOP: { stop(); break; } case MSG_UPDATE_CLIENT_STATE: { updateClientState(); break; } case MSG_SELECT_REMOTE_ROUTE: { Pair<Integer, String> obj = (Pair<Integer, String>) msg.obj; selectRemoteRoute(obj.first, obj.second); break; } case MSG_UPDATE_CLIENT_USAGE: { updateClientUsage(); break; } } } private void start() { if (!mRunning) { mRunning = true; mWatcher.start(); } } private void stop() { if (mRunning) { mRunning = false; //TODO: may unselect routes mWatcher.stop(); // also stops all providers } } @Override public void addProvider(MediaRoute2ProviderProxy provider) { provider.setCallback(this); mMediaProviders.add(provider); } @Override public void removeProvider(MediaRoute2ProviderProxy provider) { mMediaProviders.remove(provider); } @Override public void onProviderStateChanged(MediaRoute2ProviderProxy provider) { updateProvider(provider); } private void updateProvider(MediaRoute2ProviderProxy provider) { scheduleUpdateClientState(); } private void selectRemoteRoute(int uid, String routeId) { if (routeId != null) { final int providerCount = mMediaProviders.size(); //TODO: should find proper provider (currently assumes a single provider) for (int i = 0; i < providerCount; ++i) { mMediaProviders.get(i).setSelectedRoute(uid, routeId); } } } private void scheduleUpdateClientState() { if (!mClientStateUpdateScheduled) { mClientStateUpdateScheduled = true; sendEmptyMessage(MSG_UPDATE_CLIENT_STATE); } } private void updateClientState() { mClientStateUpdateScheduled = false; MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return; } //TODO: send provider info int selectedUid = 0; String selectedRouteId = null; final int mediaCount = mMediaProviders.size(); for (int i = 0; i < mediaCount; i++) { selectedUid = mMediaProviders.get(i).mSelectedUid; selectedRouteId = mMediaProviders.get(i).mSelectedRouteId; } try { synchronized (service.mLock) { final int count = mUserRecord.mManagerRecords.size(); for (int i = 0; i < count; i++) { mTempManagers.add(mUserRecord.mManagerRecords.get(i).mClient); } } //TODO: Call proper callbacks when provider descriptor is implemented. final int count = mTempManagers.size(); for (int i = 0; i < count; i++) { try { mTempManagers.get(i).onRouteSelected(selectedUid, selectedRouteId); } catch (RemoteException ex) { Slog.w(TAG, "Failed to call onStateChanged. Manager probably died.", ex); } } } finally { mTempManagers.clear(); } } private void updateClientUsage() { //TODO: merge these code to updateClientState() // List<IMediaRouter2ManagerClient> 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); // } // } } } } services/core/java/com/android/server/media/MediaRouterService.java +10 −268 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java 0 → 100644 +438 −0 Original line number Diff line number Diff line /* * Copyright 2019 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.server.media; import android.app.ActivityManager; import android.content.Context; import android.media.IMediaRouter2ManagerClient; import android.media.IMediaRouterClient; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * TODO: Merge this to MediaRouterService once it's finished. */ class MediaRouter2ServiceImpl { private static final String TAG = "MediaRouter2ServiceImpl"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final Context mContext; private final Object mLock = new Object(); private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>(); private int mCurrentUserId = -1; MediaRouter2ServiceImpl(Context context) { mContext = context; } public void registerManagerAsUser(IMediaRouter2ManagerClient client, String packageName, int userId) { if (client == null) { throw new IllegalArgumentException("client must not be null"); } //TODO: should check permission final boolean trusted = true; final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, false /*allowAll*/, true /*requireFull*/, "registerManagerAsUser", packageName); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { registerManagerLocked(client, uid, pid, packageName, resolvedUserId, trusted); } } finally { Binder.restoreCallingIdentity(token); } } public void unregisterManager(IMediaRouter2ManagerClient client) { if (client == null) { throw new IllegalArgumentException("client must not be null"); } final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { unregisterManagerLocked(client, false); } } finally { Binder.restoreCallingIdentity(token); } } public void setControlCategories(IMediaRouterClient client, List<String> categories) { if (client == null) { throw new IllegalArgumentException("client must not be null"); } final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { setControlCategoriesLocked(client, categories); } } finally { Binder.restoreCallingIdentity(token); } } public void setRemoteRoute(IMediaRouter2ManagerClient client, int uid, String routeId, boolean explicit) { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { setRemoteRouteLocked(client, uid, routeId, explicit); } } finally { Binder.restoreCallingIdentity(token); } } void clientDied(ManagerRecord managerRecord) { synchronized (mLock) { unregisterManagerLocked(managerRecord.mClient, true); } } private void registerManagerLocked(IMediaRouter2ManagerClient client, int uid, int pid, String packageName, int userId, boolean trusted) { final IBinder binder = client.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); if (managerRecord == null) { boolean newUser = false; UserRecord userRecord = mUserRecords.get(userId); if (userRecord == null) { userRecord = new UserRecord(userId); newUser = true; } managerRecord = new ManagerRecord(userRecord, client, uid, pid, packageName, trusted); try { binder.linkToDeath(managerRecord, 0); } catch (RemoteException ex) { throw new RuntimeException("Media router client died prematurely.", ex); } if (newUser) { mUserRecords.put(userId, userRecord); initializeUserLocked(userRecord); } userRecord.mManagerRecords.add(managerRecord); mAllManagerRecords.put(binder, managerRecord); //TODO: send client's info to manager } } private void unregisterManagerLocked(IMediaRouter2ManagerClient 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 setRemoteRouteLocked(IMediaRouter2ManagerClient client, int uid, String routeId, boolean explicit) { ManagerRecord managerRecord = mAllManagerRecords.get(client.asBinder()); if (managerRecord != null) { if (explicit && managerRecord.mTrusted) { Pair<Integer, String> obj = new Pair<>(uid, routeId); managerRecord.mUserRecord.mHandler.obtainMessage( UserHandler.MSG_SELECT_REMOTE_ROUTE, obj).sendToTarget(); } } } 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(); // } } private void initializeUserLocked(UserRecord userRecord) { if (DEBUG) { Slog.d(TAG, userRecord + ": Initialized"); } if (userRecord.mUserId == mCurrentUserId) { userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START); } } private void disposeUserIfNeededLocked(UserRecord userRecord) { // If there are no records left and the user is no longer current then go ahead // and purge the user record and all of its associated state. If the user is current // 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.mManagerRecords.isEmpty()) { if (DEBUG) { Slog.d(TAG, userRecord + ": Disposed"); } mUserRecords.remove(userRecord.mUserId); // Note: User already stopped (by switchUser) so no need to send stop message here. } } final class UserRecord { public final int mUserId; final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>(); final UserHandler mHandler; UserRecord(int userId) { mUserId = userId; mHandler = new UserHandler(MediaRouter2ServiceImpl.this, this); } } final class ManagerRecord implements IBinder.DeathRecipient { public final UserRecord mUserRecord; public final IMediaRouter2ManagerClient mClient; public final int mUid; public final int mPid; public final String mPackageName; public final boolean mTrusted; ManagerRecord(UserRecord userRecord, IMediaRouter2ManagerClient client, int uid, int pid, String packageName, boolean trusted) { mUserRecord = userRecord; mClient = client; mUid = uid; mPid = pid; mPackageName = packageName; mTrusted = trusted; } public void dispose() { mClient.asBinder().unlinkToDeath(this, 0); } @Override public void binderDied() { clientDied(this); } public void dump(PrintWriter pw, String prefix) { pw.println(prefix + this); final String indent = prefix + " "; pw.println(indent + "mTrusted=" + mTrusted); } @Override public String toString() { return "Client " + mPackageName + " (pid " + mPid + ")"; } } static final class UserHandler extends Handler implements MediaRoute2ProviderWatcher.Callback, MediaRoute2ProviderProxy.Callback { //TODO: Should be rearranged public static final int MSG_START = 1; public static final int MSG_STOP = 2; private static final int MSG_UPDATE_CLIENT_STATE = 8; private static final int MSG_SELECT_REMOTE_ROUTE = 10; private static final int MSG_UPDATE_CLIENT_USAGE = 11; private final WeakReference<MediaRouter2ServiceImpl> mServiceRef; private final UserRecord mUserRecord; private final MediaRoute2ProviderWatcher mWatcher; private final ArrayList<IMediaRouter2ManagerClient> mTempManagers = new ArrayList<>(); private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders = new ArrayList<>(); private boolean mRunning; private boolean mClientStateUpdateScheduled; UserHandler(MediaRouter2ServiceImpl service, UserRecord userRecord) { mServiceRef = new WeakReference<>(service); mUserRecord = userRecord; mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, this, mUserRecord.mUserId); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_START: { start(); break; } case MSG_STOP: { stop(); break; } case MSG_UPDATE_CLIENT_STATE: { updateClientState(); break; } case MSG_SELECT_REMOTE_ROUTE: { Pair<Integer, String> obj = (Pair<Integer, String>) msg.obj; selectRemoteRoute(obj.first, obj.second); break; } case MSG_UPDATE_CLIENT_USAGE: { updateClientUsage(); break; } } } private void start() { if (!mRunning) { mRunning = true; mWatcher.start(); } } private void stop() { if (mRunning) { mRunning = false; //TODO: may unselect routes mWatcher.stop(); // also stops all providers } } @Override public void addProvider(MediaRoute2ProviderProxy provider) { provider.setCallback(this); mMediaProviders.add(provider); } @Override public void removeProvider(MediaRoute2ProviderProxy provider) { mMediaProviders.remove(provider); } @Override public void onProviderStateChanged(MediaRoute2ProviderProxy provider) { updateProvider(provider); } private void updateProvider(MediaRoute2ProviderProxy provider) { scheduleUpdateClientState(); } private void selectRemoteRoute(int uid, String routeId) { if (routeId != null) { final int providerCount = mMediaProviders.size(); //TODO: should find proper provider (currently assumes a single provider) for (int i = 0; i < providerCount; ++i) { mMediaProviders.get(i).setSelectedRoute(uid, routeId); } } } private void scheduleUpdateClientState() { if (!mClientStateUpdateScheduled) { mClientStateUpdateScheduled = true; sendEmptyMessage(MSG_UPDATE_CLIENT_STATE); } } private void updateClientState() { mClientStateUpdateScheduled = false; MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return; } //TODO: send provider info int selectedUid = 0; String selectedRouteId = null; final int mediaCount = mMediaProviders.size(); for (int i = 0; i < mediaCount; i++) { selectedUid = mMediaProviders.get(i).mSelectedUid; selectedRouteId = mMediaProviders.get(i).mSelectedRouteId; } try { synchronized (service.mLock) { final int count = mUserRecord.mManagerRecords.size(); for (int i = 0; i < count; i++) { mTempManagers.add(mUserRecord.mManagerRecords.get(i).mClient); } } //TODO: Call proper callbacks when provider descriptor is implemented. final int count = mTempManagers.size(); for (int i = 0; i < count; i++) { try { mTempManagers.get(i).onRouteSelected(selectedUid, selectedRouteId); } catch (RemoteException ex) { Slog.w(TAG, "Failed to call onStateChanged. Manager probably died.", ex); } } } finally { mTempManagers.clear(); } } private void updateClientUsage() { //TODO: merge these code to updateClientState() // List<IMediaRouter2ManagerClient> 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); // } // } } } }
services/core/java/com/android/server/media/MediaRouterService.java +10 −268 File changed.Preview size limit exceeded, changes collapsed. Show changes