ChromiumSyncAdapter.java revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.app.Application; 9import android.content.AbstractThreadedSyncAdapter; 10import android.content.ContentProviderClient; 11import android.content.Context; 12import android.content.SyncResult; 13import android.os.Bundle; 14import android.os.Handler; 15import android.util.Log; 16 17import com.google.common.annotations.VisibleForTesting; 18import com.google.protos.ipc.invalidation.Types; 19 20import org.chromium.base.ThreadUtils; 21import org.chromium.base.library_loader.ProcessInitException; 22import org.chromium.content.browser.BrowserStartupController; 23 24import java.util.concurrent.Semaphore; 25 26/** 27 * A sync adapter for Chromium. 28 */ 29public 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} 181