summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vlad Popa <pvlad@google.com> 2022-10-01 15:38:03 +0000
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2022-10-01 15:38:03 +0000
commita01602ca2e26829624f977bf0e7c76617310f6bd (patch)
treeb818e98e89f20fe2d1a250445b7f12807f9a095a
parent63ec6b919a7d58046449799b54ec16f3f2cccd61 (diff)
parent9e2b8ba3c31733cb4d1d2cc6de17c0e415d07d5c (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.java159
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());
}
}