change ObserverNode.binderDied() to lock the root node before manipulating
the observers tree by calling removeObserverLocked() http://b/issue?id=2457485
This commit is contained in:
@@ -32,7 +32,6 @@ import android.Manifest;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -104,7 +103,7 @@ public final class ContentService extends IContentService.Stub {
|
||||
throw new IllegalArgumentException("You must pass a valid uri and observer");
|
||||
}
|
||||
synchronized (mRootNode) {
|
||||
mRootNode.addObserver(uri, observer, notifyForDescendents);
|
||||
mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode);
|
||||
if (Config.LOGV) Log.v(TAG, "Registered observer " + observer + " at " + uri +
|
||||
" with notifyForDescendents " + notifyForDescendents);
|
||||
}
|
||||
@@ -115,7 +114,7 @@ public final class ContentService extends IContentService.Stub {
|
||||
throw new IllegalArgumentException("You must pass a valid observer");
|
||||
}
|
||||
synchronized (mRootNode) {
|
||||
mRootNode.removeObserver(observer);
|
||||
mRootNode.removeObserverLocked(observer);
|
||||
if (Config.LOGV) Log.v(TAG, "Unregistered observer " + observer);
|
||||
}
|
||||
}
|
||||
@@ -132,7 +131,7 @@ public final class ContentService extends IContentService.Stub {
|
||||
try {
|
||||
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
|
||||
synchronized (mRootNode) {
|
||||
mRootNode.collectObservers(uri, 0, observer, observerWantsSelfNotifications,
|
||||
mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
|
||||
calls);
|
||||
}
|
||||
final int numCalls = calls.size();
|
||||
@@ -470,10 +469,12 @@ public final class ContentService extends IContentService.Stub {
|
||||
*/
|
||||
public static final class ObserverNode {
|
||||
private class ObserverEntry implements IBinder.DeathRecipient {
|
||||
public IContentObserver observer;
|
||||
public boolean notifyForDescendents;
|
||||
public final IContentObserver observer;
|
||||
public final boolean notifyForDescendents;
|
||||
private final Object observersLock;
|
||||
|
||||
public ObserverEntry(IContentObserver o, boolean n) {
|
||||
public ObserverEntry(IContentObserver o, boolean n, Object observersLock) {
|
||||
this.observersLock = observersLock;
|
||||
observer = o;
|
||||
notifyForDescendents = n;
|
||||
try {
|
||||
@@ -484,7 +485,9 @@ public final class ContentService extends IContentService.Stub {
|
||||
}
|
||||
|
||||
public void binderDied() {
|
||||
removeObserver(observer);
|
||||
synchronized (observersLock) {
|
||||
removeObserverLocked(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,16 +522,16 @@ public final class ContentService extends IContentService.Stub {
|
||||
return uri.getPathSegments().size() + 1;
|
||||
}
|
||||
|
||||
public void addObserver(Uri uri, IContentObserver observer, boolean notifyForDescendents) {
|
||||
addObserver(uri, 0, observer, notifyForDescendents);
|
||||
public void addObserverLocked(Uri uri, IContentObserver observer,
|
||||
boolean notifyForDescendents, Object observersLock) {
|
||||
addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock);
|
||||
}
|
||||
|
||||
private void addObserver(Uri uri, int index, IContentObserver observer,
|
||||
boolean notifyForDescendents) {
|
||||
|
||||
private void addObserverLocked(Uri uri, int index, IContentObserver observer,
|
||||
boolean notifyForDescendents, Object observersLock) {
|
||||
// If this is the leaf node add the observer
|
||||
if (index == countUriSegments(uri)) {
|
||||
mObservers.add(new ObserverEntry(observer, notifyForDescendents));
|
||||
mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -538,7 +541,7 @@ public final class ContentService extends IContentService.Stub {
|
||||
for (int i = 0; i < N; i++) {
|
||||
ObserverNode node = mChildren.get(i);
|
||||
if (node.mName.equals(segment)) {
|
||||
node.addObserver(uri, index + 1, observer, notifyForDescendents);
|
||||
node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -546,13 +549,13 @@ public final class ContentService extends IContentService.Stub {
|
||||
// No child found, create one
|
||||
ObserverNode node = new ObserverNode(segment);
|
||||
mChildren.add(node);
|
||||
node.addObserver(uri, index + 1, observer, notifyForDescendents);
|
||||
node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
|
||||
}
|
||||
|
||||
public boolean removeObserver(IContentObserver observer) {
|
||||
public boolean removeObserverLocked(IContentObserver observer) {
|
||||
int size = mChildren.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
boolean empty = mChildren.get(i).removeObserver(observer);
|
||||
boolean empty = mChildren.get(i).removeObserverLocked(observer);
|
||||
if (empty) {
|
||||
mChildren.remove(i);
|
||||
i--;
|
||||
@@ -578,10 +581,8 @@ public final class ContentService extends IContentService.Stub {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void collectMyObservers(Uri uri,
|
||||
boolean leaf, IContentObserver observer, boolean selfNotify,
|
||||
ArrayList<ObserverCall> calls)
|
||||
{
|
||||
private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
|
||||
boolean selfNotify, ArrayList<ObserverCall> calls) {
|
||||
int N = mObservers.size();
|
||||
IBinder observerBinder = observer == null ? null : observer.asBinder();
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -600,17 +601,17 @@ public final class ContentService extends IContentService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
public void collectObservers(Uri uri, int index, IContentObserver observer,
|
||||
public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
|
||||
boolean selfNotify, ArrayList<ObserverCall> calls) {
|
||||
String segment = null;
|
||||
int segmentCount = countUriSegments(uri);
|
||||
if (index >= segmentCount) {
|
||||
// This is the leaf node, notify all observers
|
||||
collectMyObservers(uri, true, observer, selfNotify, calls);
|
||||
collectMyObserversLocked(true, observer, selfNotify, calls);
|
||||
} else if (index < segmentCount){
|
||||
segment = getUriSegment(uri, index);
|
||||
// Notify any observers at this level who are interested in descendents
|
||||
collectMyObservers(uri, false, observer, selfNotify, calls);
|
||||
collectMyObserversLocked(false, observer, selfNotify, calls);
|
||||
}
|
||||
|
||||
int N = mChildren.size();
|
||||
@@ -618,7 +619,7 @@ public final class ContentService extends IContentService.Stub {
|
||||
ObserverNode node = mChildren.get(i);
|
||||
if (segment == null || node.mName.equals(segment)) {
|
||||
// We found the child,
|
||||
node.collectObservers(uri, index + 1, observer, selfNotify, calls);
|
||||
node.collectObserversLocked(uri, index + 1, observer, selfNotify, calls);
|
||||
if (segment != null) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -24,50 +24,49 @@ import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
public class ObserverNodeTest extends AndroidTestCase {
|
||||
public class ObserverNodeTest extends AndroidTestCase {
|
||||
static class TestObserver extends ContentObserver {
|
||||
public TestObserver() {
|
||||
super(new Handler());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testUri() {
|
||||
ObserverNode root = new ObserverNode("");
|
||||
Uri[] uris = new Uri[] {
|
||||
Uri.parse("content://c/a/"),
|
||||
Uri.parse("content://c/"),
|
||||
Uri.parse("content://x/"),
|
||||
Uri.parse("content://x/"),
|
||||
Uri.parse("content://c/b/"),
|
||||
Uri.parse("content://c/a/a1/1/"),
|
||||
Uri.parse("content://c/a/a1/2/"),
|
||||
Uri.parse("content://c/b/1/"),
|
||||
Uri.parse("content://c/b/2/"),
|
||||
};
|
||||
|
||||
|
||||
int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3};
|
||||
|
||||
|
||||
// special case
|
||||
root.addObserver(uris[0], new TestObserver().getContentObserver(), false);
|
||||
root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root);
|
||||
for(int i = 1; i < uris.length; i++) {
|
||||
root.addObserver(uris[i], new TestObserver().getContentObserver(), true);
|
||||
root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root);
|
||||
}
|
||||
|
||||
|
||||
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
|
||||
|
||||
|
||||
for (int i = nums.length - 1; i >=0; --i) {
|
||||
root.collectObservers(uris[i], 0, null, false, calls);
|
||||
root.collectObserversLocked(uris[i], 0, null, false, calls);
|
||||
assertEquals(nums[i], calls.size());
|
||||
calls.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testUriNotNotify() {
|
||||
ObserverNode root = new ObserverNode("");
|
||||
Uri[] uris = new Uri[] {
|
||||
Uri.parse("content://c/"),
|
||||
Uri.parse("content://x/"),
|
||||
Uri.parse("content://x/"),
|
||||
Uri.parse("content://c/a/"),
|
||||
Uri.parse("content://c/b/"),
|
||||
Uri.parse("content://c/a/1/"),
|
||||
@@ -76,15 +75,15 @@ public class ObserverNodeTest extends AndroidTestCase {
|
||||
Uri.parse("content://c/b/2/"),
|
||||
};
|
||||
int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1};
|
||||
|
||||
|
||||
for(int i = 0; i < uris.length; i++) {
|
||||
root.addObserver(uris[i], new TestObserver().getContentObserver(), false);
|
||||
root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root);
|
||||
}
|
||||
|
||||
|
||||
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
|
||||
|
||||
|
||||
for (int i = uris.length - 1; i >=0; --i) {
|
||||
root.collectObservers(uris[i], 0, null, false, calls);
|
||||
root.collectObserversLocked(uris[i], 0, null, false, calls);
|
||||
assertEquals(nums[i], calls.size());
|
||||
calls.clear();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user