OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.sync.test.util; | |
6 | |
7 | |
8 import android.accounts.Account; | |
9 import android.content.ContentResolver; | |
10 import android.content.SyncStatusObserver; | |
11 import android.os.AsyncTask; | |
12 import android.os.Bundle; | |
13 | |
14 import junit.framework.Assert; | |
15 | |
16 import org.chromium.base.ThreadUtils; | |
17 import org.chromium.base.VisibleForTesting; | |
18 import org.chromium.sync.SyncContentResolverDelegate; | |
19 | |
20 import java.util.HashMap; | |
21 import java.util.HashSet; | |
22 import java.util.Map; | |
23 import java.util.Set; | |
24 import java.util.concurrent.Semaphore; | |
25 import java.util.concurrent.TimeUnit; | |
26 | |
27 | |
28 /** | |
29 * Mock implementation of the {@link SyncContentResolverDelegate}. | |
30 * | |
31 * This implementation only supports status change listeners for the type | |
32 * SYNC_OBSERVER_TYPE_SETTINGS. | |
33 */ | |
34 public class MockSyncContentResolverDelegate implements SyncContentResolverDeleg
ate { | |
35 | |
36 private final Set<String> mSyncAutomaticallySet; | |
37 private final Map<String, Boolean> mIsSyncableMap; | |
38 private final Object mSyncableMapLock = new Object(); | |
39 | |
40 private final Set<AsyncSyncStatusObserver> mObservers; | |
41 | |
42 private boolean mMasterSyncAutomatically; | |
43 private boolean mDisableObserverNotifications; | |
44 | |
45 private Semaphore mPendingObserverCount; | |
46 | |
47 public MockSyncContentResolverDelegate() { | |
48 mSyncAutomaticallySet = new HashSet<String>(); | |
49 mIsSyncableMap = new HashMap<String, Boolean>(); | |
50 mObservers = new HashSet<AsyncSyncStatusObserver>(); | |
51 } | |
52 | |
53 @Override | |
54 public Object addStatusChangeListener(int mask, SyncStatusObserver callback)
{ | |
55 if (mask != ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS) { | |
56 throw new IllegalArgumentException("This implementation only support
s " | |
57 + "ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS as the mask")
; | |
58 } | |
59 AsyncSyncStatusObserver asyncSyncStatusObserver = new AsyncSyncStatusObs
erver(callback); | |
60 synchronized (mObservers) { | |
61 mObservers.add(asyncSyncStatusObserver); | |
62 } | |
63 return asyncSyncStatusObserver; | |
64 } | |
65 | |
66 @Override | |
67 public void removeStatusChangeListener(Object handle) { | |
68 synchronized (mObservers) { | |
69 mObservers.remove(handle); | |
70 } | |
71 } | |
72 | |
73 @Override | |
74 @VisibleForTesting | |
75 public void setMasterSyncAutomatically(boolean sync) { | |
76 if (mMasterSyncAutomatically == sync) return; | |
77 | |
78 mMasterSyncAutomatically = sync; | |
79 notifyObservers(); | |
80 } | |
81 | |
82 @Override | |
83 public boolean getMasterSyncAutomatically() { | |
84 return mMasterSyncAutomatically; | |
85 } | |
86 | |
87 @Override | |
88 public boolean getSyncAutomatically(Account account, String authority) { | |
89 String key = createKey(account, authority); | |
90 synchronized (mSyncableMapLock) { | |
91 return mSyncAutomaticallySet.contains(key); | |
92 } | |
93 } | |
94 | |
95 @Override | |
96 public void setSyncAutomatically(Account account, String authority, boolean
sync) { | |
97 String key = createKey(account, authority); | |
98 synchronized (mSyncableMapLock) { | |
99 if (!mIsSyncableMap.containsKey(key) || !mIsSyncableMap.get(key)) { | |
100 throw new IllegalArgumentException("Account " + account | |
101 + " is not syncable for authority " + authority | |
102 + ". Can not set sync state to " + sync); | |
103 } | |
104 if (sync) { | |
105 mSyncAutomaticallySet.add(key); | |
106 } else if (mSyncAutomaticallySet.contains(key)) { | |
107 mSyncAutomaticallySet.remove(key); | |
108 } | |
109 } | |
110 notifyObservers(); | |
111 } | |
112 | |
113 @Override | |
114 public void setIsSyncable(Account account, String authority, int syncable) { | |
115 String key = createKey(account, authority); | |
116 | |
117 synchronized (mSyncableMapLock) { | |
118 switch (syncable) { | |
119 case 0: | |
120 mIsSyncableMap.put(key, false); | |
121 break; | |
122 case 1: | |
123 mIsSyncableMap.put(key, true); | |
124 break; | |
125 case -1: | |
126 if (mIsSyncableMap.containsKey(key)) { | |
127 mIsSyncableMap.remove(key); | |
128 } | |
129 break; | |
130 default: | |
131 throw new IllegalArgumentException("Unable to understand syn
cable argument: " | |
132 + syncable); | |
133 } | |
134 } | |
135 notifyObservers(); | |
136 } | |
137 | |
138 @Override | |
139 public int getIsSyncable(Account account, String authority) { | |
140 String key = createKey(account, authority); | |
141 synchronized (mSyncableMapLock) { | |
142 if (mIsSyncableMap.containsKey(key)) { | |
143 return mIsSyncableMap.get(key) ? 1 : 0; | |
144 } else { | |
145 return -1; | |
146 } | |
147 } | |
148 } | |
149 | |
150 @Override | |
151 public void removePeriodicSync(Account account, String authority, Bundle ext
ras) { | |
152 } | |
153 | |
154 private static String createKey(Account account, String authority) { | |
155 return account.name + "@@@" + account.type + "@@@" + authority; | |
156 } | |
157 | |
158 private void notifyObservers() { | |
159 if (mDisableObserverNotifications) return; | |
160 synchronized (mObservers) { | |
161 mPendingObserverCount = new Semaphore(1 - mObservers.size()); | |
162 for (AsyncSyncStatusObserver observer : mObservers) { | |
163 observer.notifyObserverAsync(mPendingObserverCount); | |
164 } | |
165 } | |
166 } | |
167 | |
168 /** | |
169 * Blocks until the last notification has been issued to all registered obse
rvers. | |
170 * Note that if an observer is removed while a notification is being handled
this can | |
171 * fail to return correctly. | |
172 * | |
173 * @throws InterruptedException | |
174 */ | |
175 @VisibleForTesting | |
176 public void waitForLastNotificationCompleted() throws InterruptedException { | |
177 Assert.assertTrue("Timed out waiting for notifications to complete.", | |
178 mPendingObserverCount.tryAcquire(5, TimeUnit.SECONDS)); | |
179 } | |
180 | |
181 public void disableObserverNotifications() { | |
182 mDisableObserverNotifications = true; | |
183 } | |
184 | |
185 /** | |
186 * Simulate an account rename, which copies settings to the new account. | |
187 */ | |
188 public void renameAccounts(Account oldAccount, Account newAccount, String au
thority) { | |
189 int oldIsSyncable = getIsSyncable(oldAccount, authority); | |
190 setIsSyncable(newAccount, authority, oldIsSyncable); | |
191 if (oldIsSyncable == 1) { | |
192 setSyncAutomatically( | |
193 newAccount, authority, getSyncAutomatically(oldAccount, auth
ority)); | |
194 } | |
195 } | |
196 | |
197 private static class AsyncSyncStatusObserver { | |
198 | |
199 private final SyncStatusObserver mSyncStatusObserver; | |
200 | |
201 private AsyncSyncStatusObserver(SyncStatusObserver syncStatusObserver) { | |
202 mSyncStatusObserver = syncStatusObserver; | |
203 } | |
204 | |
205 private void notifyObserverAsync(final Semaphore pendingObserverCount) { | |
206 if (ThreadUtils.runningOnUiThread()) { | |
207 new AsyncTask<Void, Void, Void>() { | |
208 @Override | |
209 protected Void doInBackground(Void... params) { | |
210 mSyncStatusObserver.onStatusChanged( | |
211 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); | |
212 return null; | |
213 } | |
214 | |
215 @Override | |
216 protected void onPostExecute(Void result) { | |
217 pendingObserverCount.release(); | |
218 } | |
219 }.execute(); | |
220 } else { | |
221 mSyncStatusObserver.onStatusChanged( | |
222 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); | |
223 pendingObserverCount.release(); | |
224 } | |
225 } | |
226 } | |
227 } | |
OLD | NEW |