| Index: chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
|
| index 0787d2c1dd8ec175c369aa5c655a4616544b4da1..a9e9f22a2013ee4a40490b2eb612271ead520b29 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
|
| @@ -25,6 +25,7 @@ import android.os.SystemClock;
|
| import android.support.customtabs.ICustomTabsCallback;
|
| import android.support.customtabs.ICustomTabsService;
|
| import android.text.TextUtils;
|
| +import android.util.SparseArray;
|
| import android.view.WindowManager;
|
|
|
| import org.chromium.base.FieldTrialList;
|
| @@ -85,6 +86,41 @@ class CustomTabsConnection extends ICustomTabsService.Stub {
|
| }
|
| }
|
|
|
| + private static final class PredictionStats {
|
| + private static final long MIN_DELAY = 100;
|
| + private static final long MAX_DELAY = 10000;
|
| + private long mLastRequestTimestamp = -1;
|
| + private long mDelayMs = MIN_DELAY;
|
| +
|
| + /**
|
| + * Updates the prediction stats and return whether prediction is allowed.
|
| + *
|
| + * The policy is:
|
| + * 1. If the client does not wait more than mDelayMs, decline the request.
|
| + * 2. If the client waits for more than mDelayMs but less than 2*mDelayMs,
|
| + * accept the request and double mDelayMs.
|
| + * 3. If the client waits for more than 2*mDelayMs, accept the request
|
| + * and reset mDelayMs.
|
| + *
|
| + * And: 100ms <= mDelayMs <= 10s.
|
| + *
|
| + * This way, if an application sends a burst of requests, it is quickly
|
| + * seriously throttled. If it stops being this way, back to normal.
|
| + */
|
| + public boolean updateStatsAndReturnIfAllowed() {
|
| + long now = SystemClock.elapsedRealtime();
|
| + long deltaMs = now - mLastRequestTimestamp;
|
| + if (deltaMs < mDelayMs) return false;
|
| + mLastRequestTimestamp = now;
|
| + if (deltaMs < 2 * mDelayMs) {
|
| + mDelayMs = Math.min(MAX_DELAY, mDelayMs * 2);
|
| + } else {
|
| + mDelayMs = MIN_DELAY;
|
| + }
|
| + return true;
|
| + }
|
| + }
|
| +
|
| private final Application mApplication;
|
| private final AtomicBoolean mWarmupHasBeenCalled = new AtomicBoolean();
|
| private ExternalPrerenderHandler mExternalPrerenderHandler;
|
| @@ -130,6 +166,9 @@ class CustomTabsConnection extends ICustomTabsService.Stub {
|
|
|
| private final Object mLock = new Object();
|
| private final Map<IBinder, SessionParams> mSessionParams = new HashMap<>();
|
| + // Prediction tracking is done by UID and not by session, since a
|
| + // mis-behaving application can create a large number of sessions.
|
| + private SparseArray<PredictionStats> mUidToPredictionsStats = new SparseArray<>();
|
|
|
| private CustomTabsConnection(Application application) {
|
| super();
|
| @@ -169,6 +208,9 @@ class CustomTabsConnection extends ICustomTabsService.Stub {
|
| return false;
|
| }
|
| mSessionParams.put(session, sessionParams);
|
| + if (mUidToPredictionsStats.get(uid) == null) {
|
| + mUidToPredictionsStats.put(uid, new PredictionStats());
|
| + }
|
| }
|
| return true;
|
| }
|
| @@ -223,6 +265,7 @@ class CustomTabsConnection extends ICustomTabsService.Stub {
|
| SessionParams sessionParams = mSessionParams.get(session);
|
| if (sessionParams == null || sessionParams.mUid != uid) return false;
|
| sessionParams.setPredictionMetrics(urlString, SystemClock.elapsedRealtime());
|
| + if (!mUidToPredictionsStats.get(uid).updateStatsAndReturnIfAllowed()) return false;
|
| }
|
| ThreadUtils.postOnUiThread(new Runnable() {
|
| @Override
|
| @@ -260,6 +303,11 @@ class CustomTabsConnection extends ICustomTabsService.Stub {
|
| elapsedTimeMs = SystemClock.elapsedRealtime()
|
| - sessionParams.getLastMayLaunchUrlTimestamp();
|
| sessionParams.setPredictionMetrics(null, 0);
|
| + if (outcome == GOOD_PREDICTION) {
|
| + // If the prediction was correct, back to the smallest
|
| + // throttling level.
|
| + mUidToPredictionsStats.put(sessionParams.mUid, new PredictionStats());
|
| + }
|
| }
|
| }
|
| RecordHistogram.recordEnumeratedHistogram(
|
| @@ -518,4 +566,11 @@ class CustomTabsConnection extends ICustomTabsService.Stub {
|
| }
|
| return screenSize;
|
| }
|
| +
|
| + @VisibleForTesting
|
| + void resetThrottling(int uid) {
|
| + synchronized (mLock) {
|
| + mUidToPredictionsStats.put(uid, new PredictionStats());
|
| + }
|
| + }
|
| }
|
|
|