OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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.gcore; |
| 6 |
| 7 import org.chromium.base.Log; |
| 8 import org.chromium.base.TraceEvent; |
| 9 import org.chromium.base.VisibleForTesting; |
| 10 import org.chromium.base.annotations.NoSideEffects; |
| 11 |
| 12 import java.util.concurrent.TimeUnit; |
| 13 |
| 14 /** |
| 15 * Base class for tasks which connects to Google Play Services using given Googl
eApiClient, |
| 16 * performs action specified in doWhenConnected method, disconnects the client a
nd cleans up |
| 17 * by invoking cleanUp method. |
| 18 * |
| 19 * <p> |
| 20 * Using the same client for tasks running in more than one thread is a serious
error, as |
| 21 * the state can then be modified while other threads are still using the client
. The |
| 22 * recommended way to use these tasks is with a {@link java.util.concurrent.Thre
adPoolExecutor} |
| 23 * having a pool size of 1. |
| 24 * </p> |
| 25 * <p> |
| 26 * This class waits {@link #CONNECTION_TIMEOUT_MS} milliseconds for connection t
o be established. |
| 27 * If connection is unsuccessful then it will retry after {@link #CONNECTION_RET
RY_TIME_MS} |
| 28 * milliseconds as long as Google Play Services is available. Number of retries
is limited to |
| 29 * {@link #RETRY_NUMBER_LIMIT}. |
| 30 * </p> |
| 31 * |
| 32 * @param <T> type of {@link ChromeGoogleApiClient} to use for the tasks |
| 33 */ |
| 34 public abstract class ConnectedTask<T extends ChromeGoogleApiClient> implements
Runnable { |
| 35 private static final String TAG = Log.makeTag("GCore"); |
| 36 |
| 37 public static final long CONNECTION_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5
); |
| 38 public static final long CONNECTION_RETRY_TIME_MS = TimeUnit.SECONDS.toMilli
s(10); |
| 39 public static final int RETRY_NUMBER_LIMIT = 5; |
| 40 |
| 41 private final T mClient; |
| 42 private int mRetryNumber; |
| 43 |
| 44 /** |
| 45 * Used for logging and tracing. |
| 46 * <ul> |
| 47 * <li>Log format: "{logPrefix}| {{@link #getName()}} {message}"</li> |
| 48 * <li>Trace format: "ConnectedTask:{logPrefix}:{traceEventName}"</li> |
| 49 * </ul> |
| 50 */ |
| 51 private final String mLogPrefix; |
| 52 |
| 53 /** |
| 54 * @param client |
| 55 * @param logPrefix used for logging and tracing. |
| 56 */ |
| 57 public ConnectedTask(T client, String logPrefix) { |
| 58 assert logPrefix != null; |
| 59 mClient = client; |
| 60 mLogPrefix = logPrefix; |
| 61 } |
| 62 |
| 63 /** Creates a connected task with an empty log prefix. */ |
| 64 @VisibleForTesting |
| 65 public ConnectedTask(T client) { |
| 66 this(client, ""); |
| 67 } |
| 68 |
| 69 /** |
| 70 * Executed with client connected to Google Play Services. |
| 71 * This method is intended to be overridden by a subclass. |
| 72 */ |
| 73 protected abstract void doWhenConnected(T client); |
| 74 |
| 75 /** |
| 76 * Returns a name of a task. Implementations should not have side effects |
| 77 * as we want to have the logging related calls removed. |
| 78 */ |
| 79 @NoSideEffects |
| 80 protected abstract String getName(); |
| 81 |
| 82 /** |
| 83 * Executed after doWhenConnected was done and client was disconnected. |
| 84 * May also be executed when Google Play Services is no longer available, wh
ich means connection |
| 85 * was unsuccessful and won't be retried. |
| 86 * This method is intended to be overridden by a subclass. |
| 87 */ |
| 88 protected void cleanUp() {} |
| 89 |
| 90 /** |
| 91 * Executed if the connection was unsuccessful. |
| 92 * This method is intended to be overridden by a subclass. |
| 93 */ |
| 94 protected void connectionFailed() {} |
| 95 |
| 96 @Override |
| 97 @VisibleForTesting |
| 98 public final void run() { |
| 99 TraceEvent.begin("GCore:" + mLogPrefix + ":run"); |
| 100 try { |
| 101 Log.d(TAG, "%s:%s started", mLogPrefix, getName()); |
| 102 if (mClient.connectWithTimeout(CONNECTION_TIMEOUT_MS)) { |
| 103 try { |
| 104 Log.d(TAG, "%s:%s connected", mLogPrefix, getName()); |
| 105 doWhenConnected(mClient); |
| 106 Log.d(TAG, "%s:%s finished", mLogPrefix, getName()); |
| 107 } finally { |
| 108 mClient.disconnect(); |
| 109 Log.d(TAG, "%s:%s disconnected", mLogPrefix, getName()); |
| 110 cleanUp(); |
| 111 Log.d(TAG, "%s:%s cleaned up", mLogPrefix, getName()); |
| 112 } |
| 113 } else { |
| 114 mRetryNumber++; |
| 115 if (mRetryNumber < RETRY_NUMBER_LIMIT && mClient.isGooglePlaySer
vicesAvailable()) { |
| 116 Log.d(TAG, "%s:%s calling retry", mLogPrefix, getName()); |
| 117 retry(this, CONNECTION_RETRY_TIME_MS); |
| 118 } else { |
| 119 connectionFailed(); |
| 120 Log.d(TAG, "%s:%s number of retries exceeded", mLogPrefix, g
etName()); |
| 121 cleanUp(); |
| 122 Log.d(TAG, "%s:%s cleaned up", mLogPrefix, getName()); |
| 123 } |
| 124 } |
| 125 } catch (RuntimeException e) { |
| 126 Log.e(TAG, "%s:%s runtime exception %s: %s", mLogPrefix, getName(), |
| 127 e.getClass().getName(), e.getMessage()); |
| 128 throw e; |
| 129 } finally { |
| 130 TraceEvent.end("GCore:" + mLogPrefix + ":run"); |
| 131 } |
| 132 } |
| 133 |
| 134 /** Method to implement to determine how to run the retry task. */ |
| 135 protected abstract void retry(Runnable task, long delayMs); |
| 136 } |
OLD | NEW |