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
5package org.chromium.chrome.browser.sync;
6
7import android.accounts.Account;
8import android.content.ContentResolver;
9import android.content.Context;
10import android.content.SharedPreferences;
11import android.os.AsyncTask;
12import android.os.Bundle;
13import android.preference.PreferenceManager;
14import android.util.Log;
15
16import org.chromium.base.ApplicationStatus;
17import org.chromium.base.VisibleForTesting;
18import org.chromium.sync.notifier.SyncStatusHelper;
19import org.chromium.sync.signin.AccountManagerHelper;
20
21/**
22 * A class for controlling when a sync should be performed immediately, and when it should be
23 * delayed until Chrome comes to the foreground again.
24 */
25public class DelayedSyncController {
26    private static final String TAG = "DelayedSyncController";
27    private static final String DELAYED_ACCOUNT_NAME = "delayed_account";
28
29    private static class LazyHolder {
30        private static final DelayedSyncController INSTANCE = new DelayedSyncController();
31    }
32
33    public static DelayedSyncController getInstance() {
34        return LazyHolder.INSTANCE;
35    }
36
37    @VisibleForTesting
38    DelayedSyncController() {}
39
40    /**
41     * Resume any syncs that were delayed while Chromium was backgrounded.
42     */
43    public boolean resumeDelayedSyncs(final Context context) {
44        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
45        String accountName = prefs.getString(DELAYED_ACCOUNT_NAME, null);
46        if (accountName == null) {
47            Log.d(TAG, "No delayed sync.");
48            return false;
49        } else {
50            Log.d(TAG, "Handling delayed sync.");
51            Account account = AccountManagerHelper.createAccountFromName(accountName);
52            requestSyncOnBackgroundThread(context, account);
53            return true;
54        }
55    }
56
57    /**
58     * Calls ContentResolver.requestSync() in a separate thread as it performs some blocking
59     * IO operations.
60     */
61    @VisibleForTesting
62    void requestSyncOnBackgroundThread(final Context context, final Account account) {
63        new AsyncTask<Void, Void, Void>() {
64            @Override
65            protected Void doInBackground(Void... unused) {
66                String contractAuthority =
67                        SyncStatusHelper.get(context).getContractAuthority();
68                ContentResolver.requestSync(account, contractAuthority, new Bundle());
69                return null;
70            }
71        }.execute();
72    }
73
74    /**
75     * Stores preferences to indicate that an invalidation has arrived, but dropped on the floor.
76     */
77    void setDelayedSync(Context ctx, String accountName) {
78        SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(ctx).edit();
79        editor.putString(DELAYED_ACCOUNT_NAME, accountName);
80        editor.apply();
81    }
82
83    /**
84     * If there is a delayed sync, it will be cleared.
85     */
86    @VisibleForTesting
87    void clearDelayedSyncs(Context context) {
88        setDelayedSync(context, null);
89    }
90
91    @VisibleForTesting
92    boolean shouldPerformSync(Context ctx, Bundle extras, Account account) {
93        boolean manualSync = isManualSync(extras);
94
95        if (manualSync || ApplicationStatus.hasVisibleActivities()) {
96            clearDelayedSyncs(ctx);
97            return true;
98        } else {
99            Log.d(TAG, "Delaying sync.");
100            setDelayedSync(ctx, account.name);
101            return false;
102        }
103    }
104
105    private static boolean isManualSync(Bundle extras) {
106        boolean manualSync = false;
107        if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) {
108            manualSync = true;
109            Log.d(TAG, "Manual sync requested.");
110        }
111        return manualSync;
112    }
113}
114