Merge "Remove calls to client while holding lock" am: 795e90b78e am: bef499347b

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2239513

Change-Id: I7388201e787bda29adf87d1628f3d0a254d9f7b3
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Vlad Popa
2022-10-01 15:08:40 +00:00
committed by Automerger Merge Worker

View File

@@ -49,6 +49,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -104,11 +105,7 @@ public final class PlaybackActivityMonitor
private static final VolumeShaper.Operation PLAY_SKIP_RAMP = private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build(); new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>(); private final ConcurrentLinkedQueue<PlayMonitorClient> mClients = new ConcurrentLinkedQueue<>();
// 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 Object mPlayerLock = new Object(); private final Object mPlayerLock = new Object();
@GuardedBy("mPlayerLock") @GuardedBy("mPlayerLock")
@@ -458,11 +455,9 @@ public final class PlaybackActivityMonitor
+ DateFormat.getTimeInstance().format(new Date())); + DateFormat.getTimeInstance().format(new Date()));
synchronized(mPlayerLock) { synchronized(mPlayerLock) {
pw.println("\n playback listeners:"); pw.println("\n playback listeners:");
synchronized(mClients) { for (PlayMonitorClient pmc : mClients) {
for (PlayMonitorClient pmc : mClients) { pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)")
pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)") + pmc.toString());
+ pmc.toString());
}
} }
pw.println("\n"); pw.println("\n");
// all players // all players
@@ -534,48 +529,33 @@ public final class PlaybackActivityMonitor
* @param iplayerReleased indicates if the change was due to a player being released * @param iplayerReleased indicates if the change was due to a player being released
*/ */
private void dispatchPlaybackChange(boolean iplayerReleased) { 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"); } if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
final List<AudioPlaybackConfiguration> configsSystem; 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. // are non-system playback activity listeners.
final List<AudioPlaybackConfiguration> configsPublic; List<AudioPlaybackConfiguration> configsPublic = null;
synchronized (mPlayerLock) { synchronized (mPlayerLock) {
if (mPlayers.isEmpty()) { if (mPlayers.isEmpty()) {
return; return;
} }
configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()); configsSystem = new ArrayList<>(mPlayers.values());
} }
synchronized (mClients) {
// was done at beginning of method, but could have changed final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
if (mClients.isEmpty()) { while (clientIterator.hasNext()) {
return; final PlayMonitorClient pmc = clientIterator.next();
} // do not spam the logs if there are problems communicating with this client
configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null; if (!pmc.reachedMaxErrorCount()) {
final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); if (pmc.isPrivileged()) {
while (clientIterator.hasNext()) { pmc.dispatchPlaybackConfigChange(configsSystem,
final PlayMonitorClient pmc = clientIterator.next(); iplayerReleased);
try { } else {
// do not spam the logs if there are problems communicating with this client if (configsPublic == null) {
if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) { configsPublic = anonymizeForPublicConsumption(configsSystem);
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);
}
} }
} catch (RemoteException e) { // non-system clients don't have the control interface IPlayer, so
pmc.mErrorCount++; // they don't need to flush commands when a player was released
Log.e(TAG, "Error (" + pmc.mErrorCount + pmc.dispatchPlaybackConfigChange(configsPublic, false);
") trying to dispatch playback config change to " + pmc, e);
} }
} }
} }
@@ -798,14 +778,9 @@ public final class PlaybackActivityMonitor
if (pcdb == null) { if (pcdb == null) {
return; return;
} }
synchronized(mClients) { final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged); if (pmc.init()) {
if (pmc.init()) { mClients.add(pmc);
if (!isPrivileged) {
mHasPublicClients = true;
}
mClients.add(pmc);
}
} }
} }
@@ -813,23 +788,14 @@ public final class PlaybackActivityMonitor
if (pcdb == null) { if (pcdb == null) {
return; return;
} }
synchronized(mClients) { final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); // iterate over the clients to remove the dispatcher
boolean hasPublicClients = false; while (clientIterator.hasNext()) {
// iterate over the clients to remove the dispatcher to remove, and reevaluate at PlayMonitorClient pmc = clientIterator.next();
// the same time if we still have a public client. if (pmc.equalsDispatcher(pcdb)) {
while (clientIterator.hasNext()) { pmc.release();
PlayMonitorClient pmc = clientIterator.next(); clientIterator.remove();
if (pcdb.asBinder().equals(pmc.mDispatcherCb.asBinder())) {
pmc.release();
clientIterator.remove();
} else {
if (!pmc.mIsPrivileged) {
hasPublicClients = true;
}
}
} }
mHasPublicClients = hasPublicClients;
} }
} }
@@ -857,24 +823,34 @@ public final class PlaybackActivityMonitor
// can afford to be static because only one PlaybackActivityMonitor ever instantiated // can afford to be static because only one PlaybackActivityMonitor ever instantiated
static PlaybackActivityMonitor sListenerDeathMonitor; 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 // 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) { PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
mDispatcherCb = pcdb; mDispatcherCb = pcdb;
mIsPrivileged = isPrivileged; mIsPrivileged = isPrivileged;
} }
@Override
public void binderDied() { public void binderDied() {
Log.w(TAG, "client died"); Log.w(TAG, "client died");
sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb); sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
} }
boolean init() { synchronized boolean init() {
if (mIsReleased) {
// Do not init after release
return false;
}
try { try {
mDispatcherCb.asBinder().linkToDeath(this, 0); mDispatcherCb.asBinder().linkToDeath(this, 0);
return true; return true;
@@ -884,8 +860,43 @@ public final class PlaybackActivityMonitor
} }
} }
void release() { synchronized void release() {
mDispatcherCb.asBinder().unlinkToDeath(this, 0); 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());
} }
} }