15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)package org.chromium.sync.test.util; 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.accounts.Account; 92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.content.ContentResolver; 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.content.SyncStatusObserver; 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import android.os.AsyncTask; 122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochimport junit.framework.Assert; 14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochimport org.chromium.base.ThreadUtils; 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import org.chromium.sync.notifier.SyncContentResolverDelegate; 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.HashMap; 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.HashSet; 202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.Map; 212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import java.util.Set; 22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochimport java.util.concurrent.Semaphore; 23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochimport java.util.concurrent.TimeUnit; 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/** 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) * Mock implementation of the {@link SyncContentResolverDelegate}. 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * This implementation only supports status change listeners for the type 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * SYNC_OBSERVER_TYPE_SETTINGS. 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */ 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)public class MockSyncContentResolverDelegate implements SyncContentResolverDelegate { 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private final Set<String> mSyncAutomaticallySet; 351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private final Map<String, Boolean> mIsSyncableMap; 361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private final Object mSyncableMapLock = new Object(); 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private final Set<AsyncSyncStatusObserver> mObservers; 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private boolean mMasterSyncAutomatically; 411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) private boolean mDisableObserverNotifications; 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch private Semaphore mPendingObserverCount; 44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public MockSyncContentResolverDelegate() { 461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mSyncAutomaticallySet = new HashSet<String>(); 471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mIsSyncableMap = new HashMap<String, Boolean>(); 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mObservers = new HashSet<AsyncSyncStatusObserver>(); 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public Object addStatusChangeListener(int mask, SyncStatusObserver callback) { 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (mask != ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS) { 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) throw new IllegalArgumentException("This implementation only supports " 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) + "ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS as the mask"); 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) AsyncSyncStatusObserver asyncSyncStatusObserver = new AsyncSyncStatusObserver(callback); 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) synchronized (mObservers) { 592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mObservers.add(asyncSyncStatusObserver); 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return asyncSyncStatusObserver; 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void removeStatusChangeListener(Object handle) { 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) synchronized (mObservers) { 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mObservers.remove(handle); 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void setMasterSyncAutomatically(boolean sync) { 737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (mMasterSyncAutomatically == sync) return; 747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mMasterSyncAutomatically = sync; 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifyObservers(); 772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public boolean getMasterSyncAutomatically() { 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return mMasterSyncAutomatically; 822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public boolean getSyncAutomatically(Account account, String authority) { 862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) String key = createKey(account, authority); 871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mSyncableMapLock) { 881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return mSyncAutomaticallySet.contains(key); 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void setSyncAutomatically(Account account, String authority, boolean sync) { 942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) String key = createKey(account, authority); 951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mSyncableMapLock) { 961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!mIsSyncableMap.containsKey(key) || !mIsSyncableMap.get(key)) { 972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) throw new IllegalArgumentException("Account " + account + 982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) " is not syncable for authority " + authority + 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ". Can not set sync state to " + sync); 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (sync) { 1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mSyncAutomaticallySet.add(key); 1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else if (mSyncAutomaticallySet.contains(key)) { 1041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mSyncAutomaticallySet.remove(key); 1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifyObservers(); 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public void setIsSyncable(Account account, String authority, int syncable) { 1127dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch String key = createKey(account, authority); 1137dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mSyncableMapLock) { 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) switch (syncable) { 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case 0: 1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (mSyncAutomaticallySet.contains(key)) { 1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mSyncAutomaticallySet.remove(key); 1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1207dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch 1211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mIsSyncableMap.put(key, false); 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case 1: 1241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mIsSyncableMap.put(key, true); 1251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) break; 1261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) case -1: 1271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (mIsSyncableMap.containsKey(key)) { 1281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mIsSyncableMap.remove(key); 1291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) default: 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) throw new IllegalArgumentException("Unable to understand syncable argument: " + 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) syncable); 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) notifyObservers(); 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) @Override 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public int getIsSyncable(Account account, String authority) { 1411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) String key = createKey(account, authority); 1421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) synchronized (mSyncableMapLock) { 1431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (mIsSyncableMap.containsKey(key)) { 1441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return mIsSyncableMap.containsKey(key) ? 1 : 0; 1451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else { 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return -1; 1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static String createKey(Account account, String authority) { 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return account.name + "@@@" + account.type + "@@@" + authority; 1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private void notifyObservers() { 1561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (mDisableObserverNotifications) return; 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) synchronized (mObservers) { 158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch mPendingObserverCount = new Semaphore(1 - mObservers.size()); 1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (AsyncSyncStatusObserver observer : mObservers) { 160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch observer.notifyObserverAsync(mPendingObserverCount); 1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 165eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch /** 166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Blocks until the last notification has been issued to all registered observers. 167eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Note that if an observer is removed while a notification is being handled this can 168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * fail to return correctly. 169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * 170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * @throws InterruptedException 171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */ 172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch public void waitForLastNotificationCompleted() throws InterruptedException { 173eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch Assert.assertTrue("Timed out waiting for notifications to complete.", 174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch mPendingObserverCount.tryAcquire(5, TimeUnit.SECONDS)); 175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 1771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) public void disableObserverNotifications() { 1781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) mDisableObserverNotifications = true; 1791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 1801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private static class AsyncSyncStatusObserver { 1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private final SyncStatusObserver mSyncStatusObserver; 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private AsyncSyncStatusObserver(SyncStatusObserver syncStatusObserver) { 1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) mSyncStatusObserver = syncStatusObserver; 1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch private void notifyObserverAsync(final Semaphore pendingObserverCount) { 190eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (ThreadUtils.runningOnUiThread()) { 191eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch new AsyncTask<Void, Void, Void>() { 192eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch @Override 193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch protected Void doInBackground(Void... params) { 194eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch mSyncStatusObserver.onStatusChanged( 195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); 196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch return null; 197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch @Override 200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch protected void onPostExecute(Void result) { 201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch pendingObserverCount.release(); 202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch }.execute(); 204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } else { 205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch mSyncStatusObserver.onStatusChanged( 206eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); 207eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch pendingObserverCount.release(); 208eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch } 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 212