diff options
| author | 2022-10-01 15:38:03 +0000 | |
|---|---|---|
| committer | 2022-10-01 15:38:03 +0000 | |
| commit | a01602ca2e26829624f977bf0e7c76617310f6bd (patch) | |
| tree | b818e98e89f20fe2d1a250445b7f12807f9a095a | |
| parent | 63ec6b919a7d58046449799b54ec16f3f2cccd61 (diff) | |
| parent | 9e2b8ba3c31733cb4d1d2cc6de17c0e415d07d5c (diff) | |
Merge "Remove calls to client while holding lock" am: 795e90b78e am: bef499347b am: 9e2b8ba3c3
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2239513
Change-Id: I2b6fff9cb7beafeff4c107db327392eae3078280
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
| -rw-r--r-- | services/core/java/com/android/server/audio/PlaybackActivityMonitor.java | 159 |
1 files changed, 85 insertions, 74 deletions
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index b3e7e31d37fc..93841fe288e1 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -49,6 +49,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; /** @@ -104,11 +105,7 @@ public final class PlaybackActivityMonitor private static final VolumeShaper.Operation PLAY_SKIP_RAMP = new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build(); - private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>(); - // a public client is one that needs an anonymized version of the playback configurations, we - // keep track of whether there is at least one to know when we need to create the list of - // playback configurations that do not contain uid/pid/package name information. - private boolean mHasPublicClients = false; + private final ConcurrentLinkedQueue<PlayMonitorClient> mClients = new ConcurrentLinkedQueue<>(); private final Object mPlayerLock = new Object(); @GuardedBy("mPlayerLock") @@ -458,11 +455,9 @@ public final class PlaybackActivityMonitor + DateFormat.getTimeInstance().format(new Date())); synchronized(mPlayerLock) { pw.println("\n playback listeners:"); - synchronized(mClients) { - for (PlayMonitorClient pmc : mClients) { - pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)") - + pmc.toString()); - } + for (PlayMonitorClient pmc : mClients) { + pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)") + + pmc.toString()); } pw.println("\n"); // all players @@ -534,48 +529,33 @@ public final class PlaybackActivityMonitor * @param iplayerReleased indicates if the change was due to a player being released */ private void dispatchPlaybackChange(boolean iplayerReleased) { - synchronized (mClients) { - // typical use case, nobody is listening, don't do any work - if (mClients.isEmpty()) { - return; - } - } if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); } final List<AudioPlaybackConfiguration> configsSystem; - // list of playback configurations for "public consumption". It is only computed if there + // list of playback configurations for "public consumption". It is computed lazy if there // are non-system playback activity listeners. - final List<AudioPlaybackConfiguration> configsPublic; + List<AudioPlaybackConfiguration> configsPublic = null; synchronized (mPlayerLock) { if (mPlayers.isEmpty()) { return; } - configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()); + configsSystem = new ArrayList<>(mPlayers.values()); } - synchronized (mClients) { - // was done at beginning of method, but could have changed - if (mClients.isEmpty()) { - return; - } - configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null; - final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); - while (clientIterator.hasNext()) { - final PlayMonitorClient pmc = clientIterator.next(); - try { - // do not spam the logs if there are problems communicating with this client - if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) { - if (pmc.mIsPrivileged) { - pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem, - iplayerReleased); - } else { - // non-system clients don't have the control interface IPlayer, so - // they don't need to flush commands when a player was released - pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false); - } + + final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); + while (clientIterator.hasNext()) { + final PlayMonitorClient pmc = clientIterator.next(); + // do not spam the logs if there are problems communicating with this client + if (!pmc.reachedMaxErrorCount()) { + if (pmc.isPrivileged()) { + pmc.dispatchPlaybackConfigChange(configsSystem, + iplayerReleased); + } else { + if (configsPublic == null) { + configsPublic = anonymizeForPublicConsumption(configsSystem); } - } catch (RemoteException e) { - pmc.mErrorCount++; - Log.e(TAG, "Error (" + pmc.mErrorCount + - ") trying to dispatch playback config change to " + pmc, e); + // non-system clients don't have the control interface IPlayer, so + // they don't need to flush commands when a player was released + pmc.dispatchPlaybackConfigChange(configsPublic, false); } } } @@ -798,14 +778,9 @@ public final class PlaybackActivityMonitor if (pcdb == null) { return; } - synchronized(mClients) { - final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged); - if (pmc.init()) { - if (!isPrivileged) { - mHasPublicClients = true; - } - mClients.add(pmc); - } + final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged); + if (pmc.init()) { + mClients.add(pmc); } } @@ -813,23 +788,14 @@ public final class PlaybackActivityMonitor if (pcdb == null) { return; } - synchronized(mClients) { - final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); - boolean hasPublicClients = false; - // iterate over the clients to remove the dispatcher to remove, and reevaluate at - // the same time if we still have a public client. - while (clientIterator.hasNext()) { - PlayMonitorClient pmc = clientIterator.next(); - if (pcdb.asBinder().equals(pmc.mDispatcherCb.asBinder())) { - pmc.release(); - clientIterator.remove(); - } else { - if (!pmc.mIsPrivileged) { - hasPublicClients = true; - } - } + final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); + // iterate over the clients to remove the dispatcher + while (clientIterator.hasNext()) { + PlayMonitorClient pmc = clientIterator.next(); + if (pmc.equalsDispatcher(pcdb)) { + pmc.release(); + clientIterator.remove(); } - mHasPublicClients = hasPublicClients; } } @@ -857,24 +823,34 @@ public final class PlaybackActivityMonitor // can afford to be static because only one PlaybackActivityMonitor ever instantiated static PlaybackActivityMonitor sListenerDeathMonitor; - final IPlaybackConfigDispatcher mDispatcherCb; - final boolean mIsPrivileged; - - int mErrorCount = 0; // number of errors after which we don't update this client anymore to not spam the logs - static final int MAX_ERRORS = 5; + private static final int MAX_ERRORS = 5; + + private final IPlaybackConfigDispatcher mDispatcherCb; + + @GuardedBy("this") + private final boolean mIsPrivileged; + @GuardedBy("this") + private boolean mIsReleased = false; + @GuardedBy("this") + private int mErrorCount = 0; PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) { mDispatcherCb = pcdb; mIsPrivileged = isPrivileged; } + @Override public void binderDied() { Log.w(TAG, "client died"); sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb); } - boolean init() { + synchronized boolean init() { + if (mIsReleased) { + // Do not init after release + return false; + } try { mDispatcherCb.asBinder().linkToDeath(this, 0); return true; @@ -884,8 +860,43 @@ public final class PlaybackActivityMonitor } } - void release() { + synchronized void release() { mDispatcherCb.asBinder().unlinkToDeath(this, 0); + mIsReleased = true; + } + + void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, + boolean flush) { + synchronized (this) { + if (mIsReleased) { + // Do not dispatch anything after release + return; + } + } + try { + mDispatcherCb.dispatchPlaybackConfigChange(configs, flush); + } catch (RemoteException e) { + synchronized (this) { + mErrorCount++; + Log.e(TAG, "Error (" + mErrorCount + + ") trying to dispatch playback config change to " + this, e); + } + } + } + + synchronized boolean isPrivileged() { + return mIsPrivileged; + } + + synchronized boolean reachedMaxErrorCount() { + return mErrorCount >= MAX_ERRORS; + } + + synchronized boolean equalsDispatcher(IPlaybackConfigDispatcher pcdb) { + if (pcdb == null) { + return false; + } + return pcdb.asBinder().equals(mDispatcherCb.asBinder()); } } |