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.
5 package org.chromium.chrome.browser.sync;
7 import android.accounts.Account;
8 import android.app.Application;
9 import android.content.AbstractThreadedSyncAdapter;
10 import android.content.ContentProviderClient;
11 import android.content.Context;
12 import android.content.SyncResult;
13 import android.os.Bundle;
14 import android.os.Handler;
15 import android.util.Log;
17 import com.google.common.annotations.VisibleForTesting;
18 import com.google.protos.ipc.invalidation.Types;
20 import org.chromium.base.ThreadUtils;
21 import org.chromium.base.library_loader.ProcessInitException;
22 import org.chromium.content.browser.BrowserStartupController;
24 import java.util.concurrent.Semaphore;
27 * A sync adapter for Chromium.
29 public abstract class ChromiumSyncAdapter extends AbstractThreadedSyncAdapter {
30 private static final String TAG = "ChromiumSyncAdapter";
32 // TODO(nyquist) Make these fields package protected once downstream sync adapter tests are
35 public static final String INVALIDATION_OBJECT_SOURCE_KEY = "objectSource";
37 public static final String INVALIDATION_OBJECT_ID_KEY = "objectId";
39 public static final String INVALIDATION_VERSION_KEY = "version";
41 public static final String INVALIDATION_PAYLOAD_KEY = "payload";
43 private final Application mApplication;
44 private final boolean mAsyncStartup;
46 public ChromiumSyncAdapter(Context context, Application application) {
47 super(context, false);
48 mApplication = application;
49 mAsyncStartup = useAsyncStartup();
52 protected abstract boolean useAsyncStartup();
54 protected abstract void initCommandLine();
57 public void onPerformSync(Account account, Bundle extras, String authority,
58 ContentProviderClient provider, SyncResult syncResult) {
59 if (!DelayedSyncController.getInstance().shouldPerformSync(getContext(), extras, account)) {
63 // Browser startup is asynchronous, so we will need to wait for startup to finish.
64 Semaphore semaphore = new Semaphore(0);
66 // Configure the callback with all the data it needs.
67 BrowserStartupController.StartupCallback callback =
68 getStartupCallback(mApplication, account, extras, syncResult, semaphore);
69 startBrowserProcess(callback, syncResult, semaphore);
72 // Wait for startup to complete.
74 } catch (InterruptedException e) {
75 Log.w(TAG, "Got InterruptedException when trying to request a sync.", e);
76 // Using numIoExceptions so Android will treat this as a soft error.
77 syncResult.stats.numIoExceptions++;
81 private void startBrowserProcess(
82 final BrowserStartupController.StartupCallback callback,
83 final SyncResult syncResult, Semaphore semaphore) {
85 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
91 BrowserStartupController.get(mApplication)
92 .startBrowserProcessesAsync(callback);
94 catch (ProcessInitException e) {
95 Log.e(TAG, "Unable to load native library.", e);
99 startBrowserProcessesSync(callback);
103 } catch (RuntimeException e) {
104 // It is still unknown why we ever experience this. See http://crbug.com/180044.
105 Log.w(TAG, "Got exception when trying to request a sync. Informing Android system.", e);
106 // Using numIoExceptions so Android will treat this as a soft error.
107 syncResult.stats.numIoExceptions++;
112 private void startBrowserProcessesSync(
113 final BrowserStartupController.StartupCallback callback) {
115 BrowserStartupController.get(mApplication).startBrowserProcessesSync(
116 BrowserStartupController.MAX_RENDERERS_LIMIT);
117 } catch (ProcessInitException e) {
118 Log.e(TAG, "Unable to load native library.", e);
121 new Handler().post(new Runnable() {
124 callback.onSuccess(false);
129 private BrowserStartupController.StartupCallback getStartupCallback(
130 final Context context, final Account acct, Bundle extras,
131 final SyncResult syncResult, final Semaphore semaphore) {
132 final boolean syncAllTypes = extras.getString(INVALIDATION_OBJECT_ID_KEY) == null;
133 final int objectSource = syncAllTypes ? 0 : extras.getInt(INVALIDATION_OBJECT_SOURCE_KEY);
134 final String objectId = syncAllTypes ? "" : extras.getString(INVALIDATION_OBJECT_ID_KEY);
135 final long version = syncAllTypes ? 0 : extras.getLong(INVALIDATION_VERSION_KEY);
136 final String payload = syncAllTypes ? "" : extras.getString(INVALIDATION_PAYLOAD_KEY);
138 return new BrowserStartupController.StartupCallback() {
140 public void onSuccess(boolean alreadyStarted) {
141 // Startup succeeded, so we can tickle the sync engine.
143 Log.v(TAG, "Received sync tickle for all types.");
144 requestSyncForAllTypes();
146 // Invalidations persisted before objectSource was added should be assumed to be
147 // for Sync objects. TODO(stepco): Remove this check once all persisted
148 // invalidations can be expected to have the objectSource.
149 int resolvedSource = objectSource;
150 if (resolvedSource == 0) {
151 resolvedSource = Types.ObjectSource.Type.CHROME_SYNC.getNumber();
153 Log.v(TAG, "Received sync tickle for " + resolvedSource + " " + objectId + ".");
154 requestSync(resolvedSource, objectId, version, payload);
160 public void onFailure() {
161 // The startup failed, so we reset the delayed sync state.
162 DelayedSyncController.getInstance().setDelayedSync(context, acct.name);
163 // Using numIoExceptions so Android will treat this as a soft error.
164 syncResult.stats.numIoExceptions++;
171 public void requestSync(int objectSource, String objectId, long version, String payload) {
172 ProfileSyncService.get(mApplication)
173 .requestSyncFromNativeChrome(objectSource, objectId, version, payload);
177 public void requestSyncForAllTypes() {
178 ProfileSyncService.get(mApplication).requestSyncFromNativeChromeForAllTypes();