Merge "Handle Concurrency issues in Connection" into nyc-dev

This commit is contained in:
Brad Ebinger
2016-06-15 22:37:44 +00:00
committed by Android (Google) Code Review
2 changed files with 85 additions and 52 deletions

View File

@@ -82,6 +82,7 @@ public abstract class Conference extends Conferenceable {
private StatusHints mStatusHints; private StatusHints mStatusHints;
private Bundle mExtras; private Bundle mExtras;
private Set<String> mPreviousExtraKeys; private Set<String> mPreviousExtraKeys;
private final Object mExtrasLock = new Object();
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() { private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@Override @Override
@@ -686,32 +687,35 @@ public abstract class Conference extends Conferenceable {
* @param extras The extras associated with this {@code Conference}. * @param extras The extras associated with this {@code Conference}.
*/ */
public final void setExtras(@Nullable Bundle extras) { public final void setExtras(@Nullable Bundle extras) {
// Add/replace any new or changed extras values. // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
putExtras(extras); // block instead of letting other threads put/remove while this method is running.
synchronized (mExtrasLock) {
// Add/replace any new or changed extras values.
putExtras(extras);
// If we have used "setExtras" in the past, compare the key set from the last invocation
// to the current one and remove any keys that went away.
if (mPreviousExtraKeys != null) {
List<String> toRemove = new ArrayList<String>();
for (String oldKey : mPreviousExtraKeys) {
if (extras == null || !extras.containsKey(oldKey)) {
toRemove.add(oldKey);
}
}
// If we have used "setExtras" in the past, compare the key set from the last invocation to if (!toRemove.isEmpty()) {
// the current one and remove any keys that went away. removeExtras(toRemove);
if (mPreviousExtraKeys != null) {
List<String> toRemove = new ArrayList<String>();
for (String oldKey : mPreviousExtraKeys) {
if (extras == null || !extras.containsKey(oldKey)) {
toRemove.add(oldKey);
} }
} }
if (!toRemove.isEmpty()) { // Track the keys the last time set called setExtras. This way, the next time setExtras
removeExtras(toRemove); // is called we can see if the caller has removed any extras values.
if (mPreviousExtraKeys == null) {
mPreviousExtraKeys = new ArraySet<String>();
}
mPreviousExtraKeys.clear();
if (extras != null) {
mPreviousExtraKeys.addAll(extras.keySet());
} }
}
// Track the keys the last time set called setExtras. This way, the next time setExtras is
// called we can see if the caller has removed any extras values.
if (mPreviousExtraKeys == null) {
mPreviousExtraKeys = new ArraySet<String>();
}
mPreviousExtraKeys.clear();
if (extras != null) {
mPreviousExtraKeys.addAll(extras.keySet());
} }
} }
@@ -730,13 +734,19 @@ public abstract class Conference extends Conferenceable {
return; return;
} }
if (mExtras == null) { // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
mExtras = new Bundle(); // onExtrasChanged.
Bundle listenersBundle;
synchronized (mExtrasLock) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putAll(extras);
listenersBundle = new Bundle(mExtras);
} }
mExtras.putAll(extras);
for (Listener l : mListeners) { for (Listener l : mListeners) {
l.onExtrasChanged(this, extras); l.onExtrasChanged(this, new Bundle(listenersBundle));
} }
} }
@@ -790,17 +800,17 @@ public abstract class Conference extends Conferenceable {
return; return;
} }
if (mExtras != null) { synchronized (mExtrasLock) {
for (String key : keys) { if (mExtras != null) {
mExtras.remove(key); for (String key : keys) {
} mExtras.remove(key);
if (mExtras.size() == 0) { }
mExtras = null;
} }
} }
List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
for (Listener l : mListeners) { for (Listener l : mListeners) {
l.onExtrasRemoved(this, keys); l.onExtrasRemoved(this, unmodifiableKeys);
} }
} }
@@ -833,7 +843,13 @@ public abstract class Conference extends Conferenceable {
* @hide * @hide
*/ */
final void handleExtrasChanged(Bundle extras) { final void handleExtrasChanged(Bundle extras) {
mExtras = extras; Bundle b = null;
onExtrasChanged(mExtras); synchronized (mExtrasLock) {
mExtras = extras;
if (mExtras != null) {
b = new Bundle(mExtras);
}
}
onExtrasChanged(b);
} }
} }

View File

@@ -1239,6 +1239,7 @@ public abstract class Connection extends Conferenceable {
private Conference mConference; private Conference mConference;
private ConnectionService mConnectionService; private ConnectionService mConnectionService;
private Bundle mExtras; private Bundle mExtras;
private final Object mExtrasLock = new Object();
/** /**
* Tracks the key set for the extras bundle provided on the last invocation of * Tracks the key set for the extras bundle provided on the last invocation of
@@ -1388,7 +1389,13 @@ public abstract class Connection extends Conferenceable {
* @return The extras associated with this connection. * @return The extras associated with this connection.
*/ */
public final Bundle getExtras() { public final Bundle getExtras() {
return mExtras; Bundle extras = null;
synchronized (mExtrasLock) {
if (mExtras != null) {
extras = new Bundle(mExtras);
}
}
return extras;
} }
/** /**
@@ -1924,14 +1931,20 @@ public abstract class Connection extends Conferenceable {
if (extras == null) { if (extras == null) {
return; return;
} }
// Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
if (mExtras == null) { // the listeners.
mExtras = new Bundle(); Bundle listenerExtras;
synchronized (mExtrasLock) {
if (mExtras == null) {
mExtras = new Bundle();
}
mExtras.putAll(extras);
listenerExtras = new Bundle(mExtras);
} }
mExtras.putAll(extras);
for (Listener l : mListeners) { for (Listener l : mListeners) {
l.onExtrasChanged(this, extras); // Create a new clone of the extras for each listener so that they don't clobber
// each other
l.onExtrasChanged(this, new Bundle(listenerExtras));
} }
} }
@@ -1981,18 +1994,16 @@ public abstract class Connection extends Conferenceable {
* @hide * @hide
*/ */
public final void removeExtras(List<String> keys) { public final void removeExtras(List<String> keys) {
if (mExtras != null) { synchronized (mExtrasLock) {
for (String key : keys) { if (mExtras != null) {
mExtras.remove(key); for (String key : keys) {
} mExtras.remove(key);
}
if (mExtras.size() == 0) {
mExtras = null;
} }
} }
List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
for (Listener l : mListeners) { for (Listener l : mListeners) {
l.onExtrasRemoved(this, keys); l.onExtrasRemoved(this, unmodifiableKeys);
} }
} }
@@ -2274,8 +2285,14 @@ public abstract class Connection extends Conferenceable {
* @hide * @hide
*/ */
final void handleExtrasChanged(Bundle extras) { final void handleExtrasChanged(Bundle extras) {
mExtras = extras; Bundle b = null;
onExtrasChanged(mExtras); synchronized (mExtrasLock) {
mExtras = extras;
if (mExtras != null) {
b = new Bundle(mExtras);
}
}
onExtrasChanged(b);
} }
/** /**