ChromiumSyncAdapter.java revision f2477e01787aa58f445919b809d89e252beef54f
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.content.browser.BrowserStartupController; 22 23import java.util.concurrent.Semaphore; 24 25/** 26 * A sync adapter for Chromium. 27 */ 28public abstract class ChromiumSyncAdapter extends AbstractThreadedSyncAdapter { 29 private static final String TAG = "ChromiumSyncAdapter"; 30 31 // TODO(nyquist) Make these fields package protected once downstream sync adapter tests are 32 // removed. 33 @VisibleForTesting 34 public static final String INVALIDATION_OBJECT_SOURCE_KEY = "objectSource"; 35 @VisibleForTesting 36 public static final String INVALIDATION_OBJECT_ID_KEY = "objectId"; 37 @VisibleForTesting 38 public static final String INVALIDATION_VERSION_KEY = "version"; 39 @VisibleForTesting 40 public static final String INVALIDATION_PAYLOAD_KEY = "payload"; 41 42 private final Application mApplication; 43 private final boolean mAsyncStartup; 44 45 public ChromiumSyncAdapter(Context context, Application application) { 46 super(context, false); 47 mApplication = application; 48 mAsyncStartup = useAsyncStartup(); 49 } 50 51 protected abstract boolean useAsyncStartup(); 52 53 protected abstract void initCommandLine(); 54 55 @Override 56 public void onPerformSync(Account account, Bundle extras, String authority, 57 ContentProviderClient provider, SyncResult syncResult) { 58 if (!DelayedSyncController.getInstance().shouldPerformSync(getContext(), extras, account)) { 59 return; 60 } 61 62 // Browser startup is asynchronous, so we will need to wait for startup to finish. 63 Semaphore semaphore = new Semaphore(0); 64 65 // Configure the callback with all the data it needs. 66 BrowserStartupController.StartupCallback callback = 67 getStartupCallback(mApplication, account, extras, syncResult, semaphore); 68 startBrowserProcess(callback, syncResult, semaphore); 69 70 try { 71 // Wait for startup to complete. 72 semaphore.acquire(); 73 } catch (InterruptedException e) { 74 Log.w(TAG, "Got InterruptedException when trying to request a sync.", e); 75 // Using numIoExceptions so Android will treat this as a soft error. 76 syncResult.stats.numIoExceptions++; 77 } 78 } 79 80 private void startBrowserProcess( 81 final BrowserStartupController.StartupCallback callback, 82 final SyncResult syncResult, Semaphore semaphore) { 83 try { 84 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 85 @Override 86 public void run() { 87 initCommandLine(); 88 if (mAsyncStartup) { 89 BrowserStartupController.get(mApplication) 90 .startBrowserProcessesAsync(callback); 91 } else { 92 startBrowserProcessesSync(callback); 93 } 94 } 95 }); 96 } catch (RuntimeException e) { 97 // It is still unknown why we ever experience this. See http://crbug.com/180044. 98 Log.w(TAG, "Got exception when trying to request a sync. Informing Android system.", e); 99 // Using numIoExceptions so Android will treat this as a soft error. 100 syncResult.stats.numIoExceptions++; 101 semaphore.release(); 102 } 103 } 104 105 private void startBrowserProcessesSync( 106 final BrowserStartupController.StartupCallback callback) { 107 if (BrowserStartupController.get(mApplication).startBrowserProcessesSync( 108 BrowserStartupController.MAX_RENDERERS_LIMIT)) { 109 new Handler().post(new Runnable() { 110 @Override 111 public void run() { 112 callback.onSuccess(false); 113 } 114 }); 115 } else { 116 Log.e(TAG, "Unable to start browser process."); 117 new Handler().post(new Runnable() { 118 @Override 119 public void run() { 120 callback.onFailure(); 121 } 122 }); 123 } 124 } 125 126 private BrowserStartupController.StartupCallback getStartupCallback( 127 final Context context, final Account acct, Bundle extras, 128 final SyncResult syncResult, final Semaphore semaphore) { 129 final boolean syncAllTypes = extras.getString(INVALIDATION_OBJECT_ID_KEY) == null; 130 final int objectSource = syncAllTypes ? 0 : extras.getInt(INVALIDATION_OBJECT_SOURCE_KEY); 131 final String objectId = syncAllTypes ? "" : extras.getString(INVALIDATION_OBJECT_ID_KEY); 132 final long version = syncAllTypes ? 0 : extras.getLong(INVALIDATION_VERSION_KEY); 133 final String payload = syncAllTypes ? "" : extras.getString(INVALIDATION_PAYLOAD_KEY); 134 135 return new BrowserStartupController.StartupCallback() { 136 @Override 137 public void onSuccess(boolean alreadyStarted) { 138 // Startup succeeded, so we can tickle the sync engine. 139 if (syncAllTypes) { 140 Log.v(TAG, "Received sync tickle for all types."); 141 requestSyncForAllTypes(); 142 } else { 143 // Invalidations persisted before objectSource was added should be assumed to be 144 // for Sync objects. TODO(stepco): Remove this check once all persisted 145 // invalidations can be expected to have the objectSource. 146 int resolvedSource = objectSource; 147 if (resolvedSource == 0) { 148 resolvedSource = Types.ObjectSource.Type.CHROME_SYNC.getNumber(); 149 } 150 Log.v(TAG, "Received sync tickle for " + resolvedSource + " " + objectId + "."); 151 requestSync(resolvedSource, objectId, version, payload); 152 } 153 semaphore.release(); 154 } 155 156 @Override 157 public void onFailure() { 158 // The startup failed, so we reset the delayed sync state. 159 DelayedSyncController.getInstance().setDelayedSync(context, acct.name); 160 // Using numIoExceptions so Android will treat this as a soft error. 161 syncResult.stats.numIoExceptions++; 162 semaphore.release(); 163 } 164 }; 165 } 166 167 @VisibleForTesting 168 public void requestSync(int objectSource, String objectId, long version, String payload) { 169 ProfileSyncService.get(mApplication) 170 .requestSyncFromNativeChrome(objectSource, objectId, version, payload); 171 } 172 173 @VisibleForTesting 174 public void requestSyncForAllTypes() { 175 ProfileSyncService.get(mApplication).requestSyncFromNativeChromeForAllTypes(); 176 } 177} 178