| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.chrome.browser.invalidation; |
| |
| import android.accounts.Account; |
| import android.content.AbstractThreadedSyncAdapter; |
| import android.content.ContentProviderClient; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.SyncResult; |
| import android.os.Bundle; |
| |
| import org.chromium.base.Log; |
| import org.chromium.base.ThreadUtils; |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.base.library_loader.ProcessInitException; |
| import org.chromium.chrome.browser.init.BrowserParts; |
| import org.chromium.chrome.browser.init.ChromeBrowserInitializer; |
| import org.chromium.chrome.browser.init.EmptyBrowserParts; |
| import org.chromium.chrome.browser.profiles.Profile; |
| import org.chromium.components.invalidation.PendingInvalidation; |
| import org.chromium.components.signin.ChromeSigninController; |
| |
| import java.util.concurrent.Semaphore; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * A Sync adapter that receives invalidations from {@link InvalidationClientService} and dispatches |
| * it to the native side with a caching layer in {@link DelayedInvalidationsController}. |
| */ |
| public class ChromeBrowserSyncAdapter extends AbstractThreadedSyncAdapter { |
| private static final String TAG = "invalidation"; |
| |
| public ChromeBrowserSyncAdapter(Context context) { |
| super(context, false); |
| } |
| |
| @Override |
| public void onPerformSync(Account account, Bundle extras, String authority, |
| ContentProviderClient provider, SyncResult syncResult) { |
| if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { |
| Account signedInAccount = ChromeSigninController.get().getSignedInUser(); |
| if (account.equals(signedInAccount)) { |
| ContentResolver.setIsSyncable(account, authority, 1); |
| } else { |
| ContentResolver.setIsSyncable(account, authority, 0); |
| } |
| return; |
| } |
| PendingInvalidation invalidation = new PendingInvalidation(extras); |
| |
| DelayedInvalidationsController controller = DelayedInvalidationsController.getInstance(); |
| if (!controller.shouldNotifyInvalidation(extras)) { |
| controller.addPendingInvalidation(account.name, invalidation); |
| return; |
| } |
| |
| // Browser startup is asynchronous, so we will need to wait for startup to finish. |
| Semaphore semaphore = new Semaphore(0); |
| |
| // Configure the BrowserParts with all the data it needs. |
| BrowserParts parts = getBrowserParts(account.name, invalidation, syncResult, semaphore); |
| startBrowserProcess(parts, syncResult, semaphore); |
| |
| try { |
| // This code is only synchronously calling a single native method |
| // to trigger and asynchronous sync cycle, so 5 minutes is generous. |
| if (!semaphore.tryAcquire(5, TimeUnit.MINUTES)) { |
| Log.w(TAG, "Sync request timed out!"); |
| syncResult.stats.numIoExceptions++; |
| } |
| } catch (InterruptedException e) { |
| Log.w(TAG, "Got InterruptedException when trying to request an invalidation.", e); |
| // Using numIoExceptions so Android will treat this as a soft error. |
| syncResult.stats.numIoExceptions++; |
| } |
| } |
| |
| private void startBrowserProcess(final BrowserParts parts, final SyncResult syncResult, |
| Semaphore semaphore) { |
| try { |
| ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
| @Override |
| public void run() { |
| ChromeBrowserInitializer.getInstance(getContext()).handlePreNativeStartup( |
| parts); |
| try { |
| ChromeBrowserInitializer.getInstance(getContext()) |
| .handlePostNativeStartup(false, parts); |
| } catch (ProcessInitException e) { |
| Log.e(TAG, "Unable to load native library.", e); |
| System.exit(-1); |
| } |
| } |
| }); |
| } catch (RuntimeException e) { |
| // It is still unknown why we ever experience this. See http://crbug.com/180044. |
| Log.w(TAG, "Got exception when trying to notify the invalidation.", e); |
| // Using numIoExceptions so Android will treat this as a soft error. |
| syncResult.stats.numIoExceptions++; |
| semaphore.release(); |
| } |
| } |
| |
| private BrowserParts getBrowserParts(final String account, |
| final PendingInvalidation invalidation, final SyncResult syncResult, |
| final Semaphore semaphore) { |
| return new EmptyBrowserParts() { |
| @Override |
| public void finishNativeInitialization() { |
| // Startup succeeded, so we can notify the invalidation. |
| notifyInvalidation(invalidation.mObjectSource, invalidation.mObjectId, |
| invalidation.mVersion, invalidation.mPayload); |
| semaphore.release(); |
| } |
| |
| @Override |
| public void onStartupFailure() { |
| // The startup failed, so we defer the invalidation. |
| DelayedInvalidationsController.getInstance().addPendingInvalidation( |
| account, invalidation); |
| // Using numIoExceptions so Android will treat this as a soft error. |
| syncResult.stats.numIoExceptions++; |
| semaphore.release(); |
| } |
| }; |
| } |
| |
| @VisibleForTesting |
| public void notifyInvalidation( |
| int objectSource, String objectId, long version, String payload) { |
| InvalidationServiceFactory.getForProfile(Profile.getLastUsedProfile()) |
| .notifyInvalidationToNativeChrome(objectSource, objectId, version, payload); |
| } |
| } |