EMMA Coverage Report (generated Fri Aug 23 16:39:17 PDT 2013)
[all classes][org.chromium.sync.notifier]

COVERAGE SUMMARY FOR SOURCE FILE [SyncStatusHelper.java]

nameclass, %method, %block, %line, %
SyncStatusHelper.java75%  (3/4)94%  (32/34)80%  (412/518)85%  (92.2/108)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SyncStatusHelper100% (1/1)91%  (21/23)75%  (255/341)82%  (57.1/70)
registerContentResolverObserver (SyncStatusHelper$SyncSettingsChangedObserver... 0%   (0/1)0%   (0/5)0%   (0/2)
unregisterContentResolverObserver (SyncStatusHelper$SyncSettingsChangedObserv... 0%   (0/1)0%   (0/5)0%   (0/2)
makeSyncable (Account): void 100% (1/1)52%  (32/61)73%  (7.3/10)
overrideSyncStatusHelperForTests (Context, SyncContentResolverDelegate): void 100% (1/1)62%  (16/26)77%  (4.6/6)
isMasterSyncAutomaticallyEnabled (): boolean 100% (1/1)67%  (10/15)67%  (2/3)
isSyncEnabledForChrome (Account): boolean 100% (1/1)67%  (14/21)62%  (2.5/4)
disableAndroidSync (Account): void 100% (1/1)74%  (14/19)93%  (3.7/4)
enableAndroidSync (Account): void 100% (1/1)77%  (17/22)95%  (4.8/5)
updateMasterSyncAutomaticallySetting (): void 100% (1/1)78%  (18/23)96%  (5.8/6)
get (Context): SyncStatusHelper 100% (1/1)79%  (19/24)87%  (4.4/5)
isSyncEnabled (Account): boolean 100% (1/1)82%  (23/28)75%  (3/4)
<static initializer> 100% (1/1)100% (5/5)100% (1/1)
SyncStatusHelper (Context, SyncContentResolverDelegate): void 100% (1/1)100% (38/38)100% (9/9)
access$000 (): StrictMode$ThreadPolicy 100% (1/1)100% (2/2)100% (1/1)
access$100 (SyncStatusHelper): String 100% (1/1)100% (3/3)100% (1/1)
access$200 (SyncStatusHelper): SyncContentResolverDelegate 100% (1/1)100% (3/3)100% (1/1)
access$500 (SyncStatusHelper): void 100% (1/1)100% (3/3)100% (1/1)
access$600 (SyncStatusHelper): SyncStatusHelper$CachedAccountSyncSettings 100% (1/1)100% (3/3)100% (1/1)
access$700 (SyncStatusHelper): Context 100% (1/1)100% (3/3)100% (1/1)
access$800 (SyncStatusHelper): ObserverList 100% (1/1)100% (3/3)100% (1/1)
getContractAuthority (): String 100% (1/1)100% (4/4)100% (1/1)
isSyncEnabled (): boolean 100% (1/1)100% (7/7)100% (1/1)
temporarilyAllowDiskWritesAndDiskReads (): StrictMode$ThreadPolicy 100% (1/1)100% (18/18)100% (6/6)
     
class SyncStatusHelper$10%   (0/1)100% (0/0)100% (0/0)100% (0/0)
     
class SyncStatusHelper$AndroidSyncSettingsChangedObserver100% (1/1)100% (3/3)78%  (43/55)81%  (7.3/9)
onStatusChanged (int): void 100% (1/1)73%  (33/45)79%  (6.3/8)
SyncStatusHelper$AndroidSyncSettingsChangedObserver (SyncStatusHelper): void 100% (1/1)100% (6/6)100% (1/1)
SyncStatusHelper$AndroidSyncSettingsChangedObserver (SyncStatusHelper, SyncSt... 100% (1/1)100% (4/4)100% (1/1)
     
class SyncStatusHelper$CachedAccountSyncSettings100% (1/1)100% (8/8)93%  (114/122)96%  (27.8/29)
<static initializer> 100% (1/1)75%  (6/8)75%  (0.8/1)
ensureSettingsAreForAccount (Account): void 100% (1/1)78%  (14/18)88%  (3.5/4)
setIsSyncable (Account): void 100% (1/1)96%  (23/24)98%  (6.8/7)
updateSyncSettingsForAccount (Account): void 100% (1/1)97%  (30/31)95%  (6.7/7)
SyncStatusHelper$CachedAccountSyncSettings (SyncStatusHelper): void 100% (1/1)100% (6/6)100% (1/1)
SyncStatusHelper$CachedAccountSyncSettings (SyncStatusHelper, SyncStatusHelpe... 100% (1/1)100% (4/4)100% (1/1)
getSyncAutomatically (Account): boolean 100% (1/1)100% (6/6)100% (2/2)
setSyncAutomatically (Account, boolean): void 100% (1/1)100% (25/25)100% (7/7)

1// Copyright (c) 2010 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 
5package org.chromium.sync.notifier;
6 
7 
8import android.accounts.Account;
9import android.content.ContentResolver;
10import android.content.Context;
11import android.content.SyncStatusObserver;
12import android.os.StrictMode;
13 
14import com.google.common.annotations.VisibleForTesting;
15 
16import org.chromium.base.ObserverList;
17import org.chromium.sync.signin.AccountManagerHelper;
18import org.chromium.sync.signin.ChromeSigninController;
19 
20import javax.annotation.concurrent.NotThreadSafe;
21import javax.annotation.concurrent.ThreadSafe;
22 
23/**
24 * A helper class to handle the current status of sync for Chrome in Android settings.
25 *
26 * It also provides an observer to be used whenever Android sync settings change.
27 *
28 * To retrieve an instance of this class, call SyncStatusHelper.get(someContext).
29 */
30@ThreadSafe
31public class SyncStatusHelper {
32 
33    /**
34     * In-memory holder of the sync configurations for a given account. On each
35     * access, updates the cache if the account has changed. This lazy-updating
36     * model is appropriate as the account changes rarely but may not be known
37     * when initially constructed. So long as we keep a single account, no
38     * expensive calls to Android are made.
39     */
40    @NotThreadSafe
41    private class CachedAccountSyncSettings {
42        private Account mAccount;
43        private boolean mSyncAutomatically;
44        private int mIsSyncable;
45 
46        private void ensureSettingsAreForAccount(Account account) {
47            assert account != null;
48            if (account.equals(mAccount)) return;
49            updateSyncSettingsForAccount(account);
50        }
51 
52        public void updateSyncSettingsForAccount(Account account) {
53            if (account == null) return;
54            mAccount = account;
55 
56            StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads();
57            mSyncAutomatically = mSyncContentResolverWrapper.getSyncAutomatically(
58                    account, mContractAuthority);
59            mIsSyncable = mSyncContentResolverWrapper.getIsSyncable(account, mContractAuthority);
60            StrictMode.setThreadPolicy(oldPolicy);
61        }
62 
63        public boolean getSyncAutomatically(Account account) {
64            ensureSettingsAreForAccount(account);
65            return mSyncAutomatically;
66        }
67 
68        public void setIsSyncable(Account account) {
69            ensureSettingsAreForAccount(account);
70            if (mIsSyncable == 0) return;
71 
72            mIsSyncable = 1;
73            StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads();
74            mSyncContentResolverWrapper.setIsSyncable(account, mContractAuthority, 1);
75            StrictMode.setThreadPolicy(oldPolicy);
76        }
77 
78        public void setSyncAutomatically(Account account, boolean value) {
79            ensureSettingsAreForAccount(account);
80            if (mSyncAutomatically == value) return;
81 
82            mSyncAutomatically = value;
83            StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads();
84            mSyncContentResolverWrapper.setSyncAutomatically(account, mContractAuthority, value);
85            StrictMode.setThreadPolicy(oldPolicy);
86        }
87    }
88 
89    @Deprecated
90    public static final String AUTH_TOKEN_TYPE_SYNC = "chromiumsync";
91 
92    // This should always have the same value as GaiaConstants::kChromeSyncOAuth2Scope.
93    public static final String CHROME_SYNC_OAUTH2_SCOPE =
94            "https://www.googleapis.com/auth/chromesync";
95 
96    public static final String TAG = "SyncStatusHelper";
97 
98    /**
99     * Lock for ensuring singleton instantiation across threads.
100     */
101    private static final Object INSTANCE_LOCK = new Object();
102 
103    private static SyncStatusHelper sSyncStatusHelper;
104 
105    private final String mContractAuthority;
106 
107    private final Context mApplicationContext;
108 
109    private final SyncContentResolverDelegate mSyncContentResolverWrapper;
110 
111    private boolean mCachedMasterSyncAutomatically;
112 
113    // Instantiation of SyncStatusHelper is guarded by a lock so volatile is unneeded.
114    private final CachedAccountSyncSettings mCachedSettings = new CachedAccountSyncSettings();
115 
116    private final ObserverList<SyncSettingsChangedObserver> mObservers =
117            new ObserverList<SyncSettingsChangedObserver>();
118 
119    /**
120     * Provides notifications when Android sync settings have changed.
121     */
122    public interface SyncSettingsChangedObserver {
123        public void syncSettingsChanged();
124    }
125 
126    /**
127     * @param context the context
128     * @param syncContentResolverWrapper an implementation of SyncContentResolverWrapper
129     */
130    private SyncStatusHelper(Context context,
131            SyncContentResolverDelegate syncContentResolverWrapper) {
132        mApplicationContext = context.getApplicationContext();
133        mSyncContentResolverWrapper = syncContentResolverWrapper;
134        mContractAuthority = getContractAuthority();
135 
136        updateMasterSyncAutomaticallySetting();
137 
138        mSyncContentResolverWrapper.addStatusChangeListener(
139                ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
140                new AndroidSyncSettingsChangedObserver());
141    }
142 
143    private void updateMasterSyncAutomaticallySetting() {
144        StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads();
145        synchronized (mCachedSettings) {
146            mCachedMasterSyncAutomatically = mSyncContentResolverWrapper
147                    .getMasterSyncAutomatically();
148        }
149        StrictMode.setThreadPolicy(oldPolicy);
150    }
151 
152    /**
153     * A factory method for the SyncStatusHelper.
154     *
155     * It is possible to override the SyncContentResolverWrapper to use in tests for the
156     * instance of the SyncStatusHelper by calling overrideSyncStatusHelperForTests(...) with
157     * your SyncContentResolverWrapper.
158     *
159     * @param context the ApplicationContext is retrieved from the context used as an argument.
160     * @return a singleton instance of the SyncStatusHelper
161     */
162    public static SyncStatusHelper get(Context context) {
163        synchronized (INSTANCE_LOCK) {
164            if (sSyncStatusHelper == null) {
165                sSyncStatusHelper = new SyncStatusHelper(context,
166                        new SystemSyncContentResolverDelegate());
167            }
168        }
169        return sSyncStatusHelper;
170    }
171 
172    /**
173     * Tests might want to consider overriding the context and SyncContentResolverWrapper so they
174     * do not use the real ContentResolver in Android.
175     *
176     * @param context the context to use
177     * @param syncContentResolverWrapper the SyncContentResolverWrapper to use
178     */
179    @VisibleForTesting
180    public static void overrideSyncStatusHelperForTests(Context context,
181            SyncContentResolverDelegate syncContentResolverWrapper) {
182        synchronized (INSTANCE_LOCK) {
183            if (sSyncStatusHelper != null) {
184                throw new IllegalStateException("SyncStatusHelper already exists");
185            }
186            sSyncStatusHelper = new SyncStatusHelper(context, syncContentResolverWrapper);
187        }
188    }
189 
190    /**
191     * Returns the contract authority to use when requesting sync.
192     */
193    public String getContractAuthority() {
194        return mApplicationContext.getPackageName();
195    }
196 
197    /**
198     * Wrapper method for the ContentResolver.addStatusChangeListener(...) when we are only
199     * interested in the settings type.
200     */
201    public void registerContentResolverObserver(SyncSettingsChangedObserver observer) {
202        mObservers.addObserver(observer);
203    }
204 
205    /**
206     * Wrapper method for the ContentResolver.removeStatusChangeListener(...).
207     */
208    public void unregisterContentResolverObserver(SyncSettingsChangedObserver observer) {
209        mObservers.removeObserver(observer);
210    }
211 
212    /**
213     * Checks whether sync is currently enabled from Chrome for a given account.
214     *
215     * It checks both the master sync for the device, and Chrome sync setting for the given account.
216     *
217     * @param account the account to check if Chrome sync is enabled on.
218     * @return true if sync is on, false otherwise
219     */
220    public boolean isSyncEnabled(Account account) {
221        if (account == null) return false;
222        synchronized (mCachedSettings) {
223            return mCachedMasterSyncAutomatically && mCachedSettings.getSyncAutomatically(account);
224        }
225    }
226 
227    /**
228     * Checks whether sync is currently enabled from Chrome for the currently signed in account.
229     *
230     * It checks both the master sync for the device, and Chrome sync setting for the given account.
231     * If no user is currently signed in it returns false.
232     *
233     * @return true if sync is on, false otherwise
234     */
235    public boolean isSyncEnabled() {
236        return isSyncEnabled(ChromeSigninController.get(mApplicationContext).getSignedInUser());
237    }
238 
239    /**
240     * Checks whether sync is currently enabled from Chrome for a given account.
241     *
242     * It checks only Chrome sync setting for the given account,
243     * and ignores the master sync setting.
244     *
245     * @param account the account to check if Chrome sync is enabled on.
246     * @return true if sync is on, false otherwise
247     */
248    public boolean isSyncEnabledForChrome(Account account) {
249        if (account == null) return false;
250        synchronized (mCachedSettings) {
251            return mCachedSettings.getSyncAutomatically(account);
252        }
253    }
254 
255    /**
256     * Checks whether the master sync flag for Android is currently set.
257     *
258     * @return true if the global master sync is on, false otherwise
259     */
260    public boolean isMasterSyncAutomaticallyEnabled() {
261        synchronized (mCachedSettings) {
262            return mCachedMasterSyncAutomatically;
263        }
264    }
265 
266    /**
267     * Make sure Chrome is syncable, and enable sync.
268     *
269     * @param account the account to enable sync on
270     */
271    public void enableAndroidSync(Account account) {
272        makeSyncable(account);
273 
274        synchronized (mCachedSettings) {
275            mCachedSettings.setSyncAutomatically(account, true);
276        }
277    }
278 
279    /**
280     * Disables Android Chrome sync
281     *
282     * @param account the account to disable Chrome sync on
283     */
284    public void disableAndroidSync(Account account) {
285        synchronized (mCachedSettings) {
286            mCachedSettings.setSyncAutomatically(account, false);
287        }
288    }
289 
290    /**
291     * Register with Android Sync Manager. This is what causes the "Chrome" option to appear in
292     * Settings -> Accounts / Sync .
293     *
294     * @param account the account to enable Chrome sync on
295     */
296    private void makeSyncable(Account account) {
297        synchronized (mCachedSettings) {
298            mCachedSettings.setIsSyncable(account);
299        }
300 
301        StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads();
302        // Disable the syncability of Chrome for all other accounts. Don't use
303        // our cache as we're touching many accounts that aren't signed in, so this saves
304        // extra calls to Android sync configuration.
305        Account[] googleAccounts = AccountManagerHelper.get(mApplicationContext).
306                getGoogleAccounts();
307        for (Account accountToSetNotSyncable : googleAccounts) {
308            if (!accountToSetNotSyncable.equals(account) &&
309                    mSyncContentResolverWrapper.getIsSyncable(
310                            accountToSetNotSyncable, mContractAuthority) > 0) {
311                mSyncContentResolverWrapper.setIsSyncable(accountToSetNotSyncable,
312                        mContractAuthority, 0);
313            }
314        }
315        StrictMode.setThreadPolicy(oldPolicy);
316    }
317 
318    /**
319     * Helper class to be used by observers whenever sync settings change.
320     *
321     * To register the observer, call SyncStatusHelper.registerObserver(...).
322     */
323    private class AndroidSyncSettingsChangedObserver implements SyncStatusObserver {
324        @Override
325        public void onStatusChanged(int which) {
326            if (ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS == which) {
327                // Sync settings have changed; update our in-memory caches
328                updateMasterSyncAutomaticallySetting();
329                synchronized (mCachedSettings) {
330                    mCachedSettings.updateSyncSettingsForAccount(
331                            ChromeSigninController.get(mApplicationContext).getSignedInUser());
332                }
333 
334                // Notify anyone else that's interested
335                for (SyncSettingsChangedObserver observer: mObservers) {
336                    observer.syncSettingsChanged();
337                }
338            }
339        }
340    }
341 
342    /**
343     * Sets a new StrictMode.ThreadPolicy based on the current one, but allows disk reads
344     * and disk writes.
345     *
346     * The return value is the old policy, which must be applied after the disk access is finished,
347     * by using StrictMode.setThreadPolicy(oldPolicy).
348     *
349     * @return the policy before allowing reads and writes.
350     */
351    private static StrictMode.ThreadPolicy temporarilyAllowDiskWritesAndDiskReads() {
352        StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
353        StrictMode.ThreadPolicy.Builder newPolicy =
354                new StrictMode.ThreadPolicy.Builder(oldPolicy);
355        newPolicy.permitDiskReads();
356        newPolicy.permitDiskWrites();
357        StrictMode.setThreadPolicy(newPolicy.build());
358        return oldPolicy;
359    }
360}

[all classes][org.chromium.sync.notifier]
EMMA 2.0.5312 (C) Vladimir Roubtsov