Index: chrome/android/java_staging/src/org/chromium/chrome/browser/gcore/ConnectedTask.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/gcore/ConnectedTask.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/gcore/ConnectedTask.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..39fcfe73b4930a646c69b5903b0b7d042489e52d |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/gcore/ConnectedTask.java |
@@ -0,0 +1,136 @@ |
+// Copyright 2015 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.gcore; |
+ |
+import org.chromium.base.Log; |
+import org.chromium.base.TraceEvent; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.base.annotations.NoSideEffects; |
+ |
+import java.util.concurrent.TimeUnit; |
+ |
+/** |
+ * Base class for tasks which connects to Google Play Services using given GoogleApiClient, |
+ * performs action specified in doWhenConnected method, disconnects the client and cleans up |
+ * by invoking cleanUp method. |
+ * |
+ * <p> |
+ * Using the same client for tasks running in more than one thread is a serious error, as |
+ * the state can then be modified while other threads are still using the client. The |
+ * recommended way to use these tasks is with a {@link java.util.concurrent.ThreadPoolExecutor} |
+ * having a pool size of 1. |
+ * </p> |
+ * <p> |
+ * This class waits {@link #CONNECTION_TIMEOUT_MS} milliseconds for connection to be established. |
+ * If connection is unsuccessful then it will retry after {@link #CONNECTION_RETRY_TIME_MS} |
+ * milliseconds as long as Google Play Services is available. Number of retries is limited to |
+ * {@link #RETRY_NUMBER_LIMIT}. |
+ * </p> |
+ * |
+ * @param <T> type of {@link ChromeGoogleApiClient} to use for the tasks |
+ */ |
+public abstract class ConnectedTask<T extends ChromeGoogleApiClient> implements Runnable { |
+ private static final String TAG = Log.makeTag("GCore"); |
+ |
+ public static final long CONNECTION_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5); |
+ public static final long CONNECTION_RETRY_TIME_MS = TimeUnit.SECONDS.toMillis(10); |
+ public static final int RETRY_NUMBER_LIMIT = 5; |
+ |
+ private final T mClient; |
+ private int mRetryNumber; |
+ |
+ /** |
+ * Used for logging and tracing. |
+ * <ul> |
+ * <li>Log format: "{logPrefix}| {{@link #getName()}} {message}"</li> |
+ * <li>Trace format: "ConnectedTask:{logPrefix}:{traceEventName}"</li> |
+ * </ul> |
+ */ |
+ private final String mLogPrefix; |
+ |
+ /** |
+ * @param client |
+ * @param logPrefix used for logging and tracing. |
+ */ |
+ public ConnectedTask(T client, String logPrefix) { |
+ assert logPrefix != null; |
+ mClient = client; |
+ mLogPrefix = logPrefix; |
+ } |
+ |
+ /** Creates a connected task with an empty log prefix. */ |
+ @VisibleForTesting |
+ public ConnectedTask(T client) { |
+ this(client, ""); |
+ } |
+ |
+ /** |
+ * Executed with client connected to Google Play Services. |
+ * This method is intended to be overridden by a subclass. |
+ */ |
+ protected abstract void doWhenConnected(T client); |
+ |
+ /** |
+ * Returns a name of a task. Implementations should not have side effects |
+ * as we want to have the logging related calls removed. |
+ */ |
+ @NoSideEffects |
+ protected abstract String getName(); |
+ |
+ /** |
+ * Executed after doWhenConnected was done and client was disconnected. |
+ * May also be executed when Google Play Services is no longer available, which means connection |
+ * was unsuccessful and won't be retried. |
+ * This method is intended to be overridden by a subclass. |
+ */ |
+ protected void cleanUp() {} |
+ |
+ /** |
+ * Executed if the connection was unsuccessful. |
+ * This method is intended to be overridden by a subclass. |
+ */ |
+ protected void connectionFailed() {} |
+ |
+ @Override |
+ @VisibleForTesting |
+ public final void run() { |
+ TraceEvent.begin("GCore:" + mLogPrefix + ":run"); |
+ try { |
+ Log.d(TAG, "%s:%s started", mLogPrefix, getName()); |
+ if (mClient.connectWithTimeout(CONNECTION_TIMEOUT_MS)) { |
+ try { |
+ Log.d(TAG, "%s:%s connected", mLogPrefix, getName()); |
+ doWhenConnected(mClient); |
+ Log.d(TAG, "%s:%s finished", mLogPrefix, getName()); |
+ } finally { |
+ mClient.disconnect(); |
+ Log.d(TAG, "%s:%s disconnected", mLogPrefix, getName()); |
+ cleanUp(); |
+ Log.d(TAG, "%s:%s cleaned up", mLogPrefix, getName()); |
+ } |
+ } else { |
+ mRetryNumber++; |
+ if (mRetryNumber < RETRY_NUMBER_LIMIT && mClient.isGooglePlayServicesAvailable()) { |
+ Log.d(TAG, "%s:%s calling retry", mLogPrefix, getName()); |
+ retry(this, CONNECTION_RETRY_TIME_MS); |
+ } else { |
+ connectionFailed(); |
+ Log.d(TAG, "%s:%s number of retries exceeded", mLogPrefix, getName()); |
+ cleanUp(); |
+ Log.d(TAG, "%s:%s cleaned up", mLogPrefix, getName()); |
+ } |
+ } |
+ } catch (RuntimeException e) { |
+ Log.e(TAG, "%s:%s runtime exception %s: %s", mLogPrefix, getName(), |
+ e.getClass().getName(), e.getMessage()); |
+ throw e; |
+ } finally { |
+ TraceEvent.end("GCore:" + mLogPrefix + ":run"); |
+ } |
+ } |
+ |
+ /** Method to implement to determine how to run the retry task. */ |
+ protected abstract void retry(Runnable task, long delayMs); |
+} |