2c7510e9e4e30c20a951470faac3297432827e28
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / sync / ChromiumSyncAdapter.java
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.chrome.browser.sync;
6
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;
16
17 import com.google.common.annotations.VisibleForTesting;
18 import com.google.protos.ipc.invalidation.Types;
19
20 import org.chromium.base.ThreadUtils;
21 import org.chromium.base.library_loader.ProcessInitException;
22 import org.chromium.content.browser.BrowserStartupController;
23
24 import java.util.concurrent.Semaphore;
25
26 /**
27  * A sync adapter for Chromium.
28  */
29 public abstract class ChromiumSyncAdapter extends AbstractThreadedSyncAdapter {
30     private static final String TAG = "ChromiumSyncAdapter";
31
32     // TODO(nyquist) Make these fields package protected once downstream sync adapter tests are
33     // removed.
34     @VisibleForTesting
35     public static final String INVALIDATION_OBJECT_SOURCE_KEY = "objectSource";
36     @VisibleForTesting
37     public static final String INVALIDATION_OBJECT_ID_KEY = "objectId";
38     @VisibleForTesting
39     public static final String INVALIDATION_VERSION_KEY = "version";
40     @VisibleForTesting
41     public static final String INVALIDATION_PAYLOAD_KEY = "payload";
42
43     private final Application mApplication;
44     private final boolean mAsyncStartup;
45
46     public ChromiumSyncAdapter(Context context, Application application) {
47         super(context, false);
48         mApplication = application;
49         mAsyncStartup = useAsyncStartup();
50     }
51
52     protected abstract boolean useAsyncStartup();
53
54     protected abstract void initCommandLine();
55
56     @Override
57     public void onPerformSync(Account account, Bundle extras, String authority,
58                               ContentProviderClient provider, SyncResult syncResult) {
59         if (!DelayedSyncController.getInstance().shouldPerformSync(getContext(), extras, account)) {
60             return;
61         }
62
63         // Browser startup is asynchronous, so we will need to wait for startup to finish.
64         Semaphore semaphore = new Semaphore(0);
65
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);
70
71         try {
72             // Wait for startup to complete.
73             semaphore.acquire();
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++;
78         }
79     }
80
81     private void startBrowserProcess(
82             final BrowserStartupController.StartupCallback callback,
83             final SyncResult syncResult, Semaphore semaphore) {
84         try {
85             ThreadUtils.runOnUiThreadBlocking(new Runnable() {
86                 @Override
87                 public void run() {
88                     initCommandLine();
89                     if (mAsyncStartup) {
90                         try {
91                             BrowserStartupController.get(mApplication)
92                                     .startBrowserProcessesAsync(callback);
93                         }
94                         catch (ProcessInitException e) {
95                             Log.e(TAG, "Unable to load native library.", e);
96                             System.exit(-1);
97                         }
98                     } else {
99                         startBrowserProcessesSync(callback);
100                     }
101                 }
102             });
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++;
108             semaphore.release();
109         }
110     }
111
112     private void startBrowserProcessesSync(
113             final BrowserStartupController.StartupCallback callback) {
114         try {
115             BrowserStartupController.get(mApplication).startBrowserProcessesSync(
116                     BrowserStartupController.MAX_RENDERERS_LIMIT);
117         } catch (ProcessInitException e) {
118             Log.e(TAG, "Unable to load native library.", e);
119             System.exit(-1);
120         }
121         new Handler().post(new Runnable() {
122             @Override
123             public void run() {
124                 callback.onSuccess(false);
125             }
126         });
127     }
128
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);
137
138         return new BrowserStartupController.StartupCallback() {
139             @Override
140             public void onSuccess(boolean alreadyStarted) {
141                 // Startup succeeded, so we can tickle the sync engine.
142                 if (syncAllTypes) {
143                     Log.v(TAG, "Received sync tickle for all types.");
144                     requestSyncForAllTypes();
145                 } else {
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();
152                     }
153                     Log.v(TAG, "Received sync tickle for " + resolvedSource + " " + objectId + ".");
154                     requestSync(resolvedSource, objectId, version, payload);
155                 }
156                 semaphore.release();
157             }
158
159             @Override
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++;
165                 semaphore.release();
166             }
167         };
168     }
169
170     @VisibleForTesting
171     public void requestSync(int objectSource, String objectId, long version, String payload) {
172         ProfileSyncService.get(mApplication)
173                 .requestSyncFromNativeChrome(objectSource, objectId, version, payload);
174     }
175
176     @VisibleForTesting
177     public void requestSyncForAllTypes() {
178         ProfileSyncService.get(mApplication).requestSyncFromNativeChromeForAllTypes();
179     }
180 }