| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.customtabs; | 5 package org.chromium.chrome.browser.customtabs; |
| 6 | 6 |
| 7 import android.content.ComponentName; | 7 import android.content.ComponentName; |
| 8 import android.content.Context; | 8 import android.content.Context; |
| 9 import android.content.Intent; | 9 import android.content.Intent; |
| 10 import android.content.ServiceConnection; | 10 import android.content.ServiceConnection; |
| 11 import android.content.pm.PackageManager; | 11 import android.content.pm.PackageManager; |
| 12 import android.os.IBinder; | 12 import android.os.IBinder; |
| 13 import android.os.RemoteException; | 13 import android.os.RemoteException; |
| 14 import android.os.SystemClock; | 14 import android.os.SystemClock; |
| 15 import android.support.customtabs.ICustomTabsCallback; | 15 import android.support.customtabs.ICustomTabsCallback; |
| 16 import android.text.TextUtils; | 16 import android.text.TextUtils; |
| 17 import android.util.SparseBooleanArray; |
| 17 | 18 |
| 18 import org.chromium.base.ThreadUtils; | 19 import org.chromium.base.ThreadUtils; |
| 20 import org.chromium.base.VisibleForTesting; |
| 19 import org.chromium.base.annotations.SuppressFBWarnings; | 21 import org.chromium.base.annotations.SuppressFBWarnings; |
| 20 import org.chromium.base.metrics.RecordHistogram; | 22 import org.chromium.base.metrics.RecordHistogram; |
| 21 import org.chromium.chrome.browser.IntentHandler; | 23 import org.chromium.chrome.browser.IntentHandler; |
| 22 import org.chromium.content_public.common.Referrer; | 24 import org.chromium.content_public.common.Referrer; |
| 23 | 25 |
| 24 import java.util.ArrayList; | 26 import java.util.ArrayList; |
| 25 import java.util.Arrays; | 27 import java.util.Arrays; |
| 26 import java.util.HashMap; | 28 import java.util.HashMap; |
| 27 import java.util.List; | 29 import java.util.List; |
| 28 import java.util.Map; | 30 import java.util.Map; |
| 29 import java.util.concurrent.TimeUnit; | 31 import java.util.concurrent.TimeUnit; |
| 30 | 32 |
| 31 /** Manages the clients' state for Custom Tabs. This class is threadsafe. */ | 33 /** Manages the clients' state for Custom Tabs. This class is threadsafe. */ |
| 32 class ClientManager { | 34 class ClientManager { |
| 33 // Values for the "CustomTabs.PredictionStatus" UMA histogram. Append-only. | 35 // Values for the "CustomTabs.PredictionStatus" UMA histogram. Append-only. |
| 34 private static final int NO_PREDICTION = 0; | 36 private static final int NO_PREDICTION = 0; |
| 35 private static final int GOOD_PREDICTION = 1; | 37 private static final int GOOD_PREDICTION = 1; |
| 36 private static final int BAD_PREDICTION = 2; | 38 private static final int BAD_PREDICTION = 2; |
| 37 private static final int PREDICTION_STATUS_COUNT = 3; | 39 private static final int PREDICTION_STATUS_COUNT = 3; |
| 40 // Values for the "CustomTabs.CalledWarmup" UMA histogram. Append-only. |
| 41 @VisibleForTesting static final int NO_SESSION_NO_WARMUP = 0; |
| 42 @VisibleForTesting static final int NO_SESSION_WARMUP = 1; |
| 43 @VisibleForTesting static final int SESSION_NO_WARMUP_ALREADY_CALLED = 2; |
| 44 @VisibleForTesting static final int SESSION_NO_WARMUP_NOT_CALLED = 3; |
| 45 @VisibleForTesting static final int SESSION_WARMUP = 4; |
| 46 @VisibleForTesting static final int SESSION_WARMUP_COUNT = 5; |
| 38 | 47 |
| 39 /** Per-session values. */ | 48 /** Per-session values. */ |
| 40 private static class SessionParams { | 49 private static class SessionParams { |
| 41 public final int uid; | 50 public final int uid; |
| 42 public final Referrer referrer; | 51 public final Referrer referrer; |
| 43 public final ICustomTabsCallback callback; | 52 public final ICustomTabsCallback callback; |
| 44 public final IBinder.DeathRecipient deathRecipient; | 53 public final IBinder.DeathRecipient deathRecipient; |
| 45 private ServiceConnection mKeepAliveConnection = null; | 54 private ServiceConnection mKeepAliveConnection = null; |
| 46 private String mPredictedUrl = null; | 55 private String mPredictedUrl = null; |
| 47 private long mLastMayLaunchUrlTimestamp = 0; | 56 private long mLastMayLaunchUrlTimestamp = 0; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 public long getLastMayLaunchUrlTimestamp() { | 90 public long getLastMayLaunchUrlTimestamp() { |
| 82 return mLastMayLaunchUrlTimestamp; | 91 return mLastMayLaunchUrlTimestamp; |
| 83 } | 92 } |
| 84 } | 93 } |
| 85 | 94 |
| 86 /** To be called when a client gets disconnected. */ | 95 /** To be called when a client gets disconnected. */ |
| 87 public interface DisconnectCallback { public void run(IBinder session); } | 96 public interface DisconnectCallback { public void run(IBinder session); } |
| 88 | 97 |
| 89 private final Context mContext; | 98 private final Context mContext; |
| 90 private final Map<IBinder, SessionParams> mSessionParams = new HashMap<>(); | 99 private final Map<IBinder, SessionParams> mSessionParams = new HashMap<>(); |
| 100 private final SparseBooleanArray mUidHasCalledWarmup = new SparseBooleanArra
y(); |
| 101 private boolean mWarmupHasBeenCalled = false; |
| 91 | 102 |
| 92 public ClientManager(Context context) { | 103 public ClientManager(Context context) { |
| 93 mContext = context.getApplicationContext(); | 104 mContext = context.getApplicationContext(); |
| 94 RequestThrottler.purgeOldEntries(mContext); | 105 RequestThrottler.purgeOldEntries(mContext); |
| 95 } | 106 } |
| 96 | 107 |
| 97 /** Creates a new session. | 108 /** Creates a new session. |
| 98 * | 109 * |
| 99 * @param cb Callback provided by the client. | 110 * @param cb Callback provided by the client. |
| 100 * @param uid Client UID, as returned by Binder.getCallingUid(), | 111 * @param uid Client UID, as returned by Binder.getCallingUid(), |
| (...skipping 24 matching lines...) Expand all Loading... |
| 125 } catch (RemoteException e) { | 136 } catch (RemoteException e) { |
| 126 // The return code doesn't matter, because this executes when | 137 // The return code doesn't matter, because this executes when |
| 127 // the caller has died. | 138 // the caller has died. |
| 128 return false; | 139 return false; |
| 129 } | 140 } |
| 130 mSessionParams.put(session, params); | 141 mSessionParams.put(session, params); |
| 131 } | 142 } |
| 132 return true; | 143 return true; |
| 133 } | 144 } |
| 134 | 145 |
| 146 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") |
| 147 public synchronized void recordUidHasCalledWarmup(int uid) { |
| 148 mWarmupHasBeenCalled = true; |
| 149 mUidHasCalledWarmup.put(uid, true); |
| 150 } |
| 151 |
| 135 /** Updates the client behavior stats and returns whether speculation is all
owed. | 152 /** Updates the client behavior stats and returns whether speculation is all
owed. |
| 136 * | 153 * |
| 137 * @param session Client session. | 154 * @param session Client session. |
| 138 * @param uid As returned by Binder.getCallingUid(). | 155 * @param uid As returned by Binder.getCallingUid(). |
| 139 * @param url Predicted URL. | 156 * @param url Predicted URL. |
| 140 * @return true if speculation is allowed. | 157 * @return true if speculation is allowed. |
| 141 */ | 158 */ |
| 142 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") | 159 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") |
| 143 public synchronized boolean updateStatsAndReturnWhetherAllowed( | 160 public synchronized boolean updateStatsAndReturnWhetherAllowed( |
| 144 IBinder session, int uid, String url) { | 161 IBinder session, int uid, String url) { |
| 145 SessionParams params = mSessionParams.get(session); | 162 SessionParams params = mSessionParams.get(session); |
| 146 if (params == null || params.uid != uid) return false; | 163 if (params == null || params.uid != uid) return false; |
| 147 params.setPredictionMetrics(url, SystemClock.elapsedRealtime()); | 164 params.setPredictionMetrics(url, SystemClock.elapsedRealtime()); |
| 148 RequestThrottler throttler = RequestThrottler.getForUid(mContext, uid); | 165 RequestThrottler throttler = RequestThrottler.getForUid(mContext, uid); |
| 149 return throttler.updateStatsAndReturnWhetherAllowed(); | 166 return throttler.updateStatsAndReturnWhetherAllowed(); |
| 150 } | 167 } |
| 151 | 168 |
| 169 @VisibleForTesting |
| 170 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") |
| 171 synchronized int getWarmupState(IBinder session) { |
| 172 SessionParams params = mSessionParams.get(session); |
| 173 boolean hasValidSession = params != null; |
| 174 boolean hasUidCalledWarmup = hasValidSession && mUidHasCalledWarmup.get(
params.uid); |
| 175 int result = mWarmupHasBeenCalled ? NO_SESSION_WARMUP : NO_SESSION_NO_WA
RMUP; |
| 176 if (hasValidSession) { |
| 177 if (hasUidCalledWarmup) { |
| 178 result = SESSION_WARMUP; |
| 179 } else { |
| 180 result = mWarmupHasBeenCalled ? SESSION_NO_WARMUP_ALREADY_CALLED |
| 181 : SESSION_NO_WARMUP_NOT_CALLED; |
| 182 } |
| 183 } |
| 184 return result; |
| 185 } |
| 186 |
| 152 /** | 187 /** |
| 153 * Registers that a client has launched a URL inside a Custom Tab. | 188 * Registers that a client has launched a URL inside a Custom Tab. |
| 154 */ | 189 */ |
| 155 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") | 190 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") |
| 156 public synchronized void registerLaunch(IBinder session, String url) { | 191 public synchronized void registerLaunch(IBinder session, String url) { |
| 157 int outcome = NO_PREDICTION; | 192 int outcome = NO_PREDICTION; |
| 158 long elapsedTimeMs = -1; | 193 long elapsedTimeMs = -1; |
| 159 SessionParams params = mSessionParams.get(session); | 194 SessionParams params = mSessionParams.get(session); |
| 160 if (params != null) { | 195 if (params != null) { |
| 161 String predictedUrl = params.getPredictedUrl(); | 196 String predictedUrl = params.getPredictedUrl(); |
| 162 outcome = predictedUrl == null ? NO_PREDICTION : predictedUrl.equals
(url) | 197 outcome = predictedUrl == null ? NO_PREDICTION : predictedUrl.equals
(url) |
| 163 ? GOOD_PREDICTION | 198 ? GOOD_PREDICTION |
| 164 : BAD_PREDICTION; | 199 : BAD_PREDICTION; |
| 165 long now = SystemClock.elapsedRealtime(); | 200 long now = SystemClock.elapsedRealtime(); |
| 166 elapsedTimeMs = now - params.getLastMayLaunchUrlTimestamp(); | 201 elapsedTimeMs = now - params.getLastMayLaunchUrlTimestamp(); |
| 167 params.setPredictionMetrics(null, 0); | 202 params.setPredictionMetrics(null, 0); |
| 168 if (outcome == GOOD_PREDICTION) { | 203 if (outcome == GOOD_PREDICTION) { |
| 169 RequestThrottler.getForUid(mContext, params.uid).registerSuccess
(url); | 204 RequestThrottler.getForUid(mContext, params.uid).registerSuccess
(url); |
| 170 } | 205 } |
| 171 } | 206 } |
| 172 RecordHistogram.recordEnumeratedHistogram( | 207 RecordHistogram.recordEnumeratedHistogram( |
| 173 "CustomTabs.PredictionStatus", outcome, PREDICTION_STATUS_COUNT)
; | 208 "CustomTabs.PredictionStatus", outcome, PREDICTION_STATUS_COUNT)
; |
| 174 if (outcome == GOOD_PREDICTION) { | 209 if (outcome == GOOD_PREDICTION) { |
| 175 RecordHistogram.recordCustomTimesHistogram("CustomTabs.PredictionToL
aunch", | 210 RecordHistogram.recordCustomTimesHistogram("CustomTabs.PredictionToL
aunch", |
| 176 elapsedTimeMs, 1, TimeUnit.MINUTES.toMillis(3), TimeUnit.MIL
LISECONDS, 100); | 211 elapsedTimeMs, 1, TimeUnit.MINUTES.toMillis(3), TimeUnit.MIL
LISECONDS, 100); |
| 177 } | 212 } |
| 213 RecordHistogram.recordEnumeratedHistogram( |
| 214 "CustomTabs.WarmupStateOnLaunch", getWarmupState(session), SESSI
ON_WARMUP_COUNT); |
| 178 } | 215 } |
| 179 | 216 |
| 180 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") | 217 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") |
| 181 public synchronized Referrer getReferrerForSession(IBinder session) { | 218 public synchronized Referrer getReferrerForSession(IBinder session) { |
| 182 SessionParams params = mSessionParams.get(session); | 219 SessionParams params = mSessionParams.get(session); |
| 183 return params != null ? params.referrer : null; | 220 return params != null ? params.referrer : null; |
| 184 } | 221 } |
| 185 | 222 |
| 186 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") | 223 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") |
| 187 public synchronized ICustomTabsCallback getCallbackForSession(IBinder sessio
n) { | 224 public synchronized ICustomTabsCallback getCallbackForSession(IBinder sessio
n) { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 public synchronized void cleanupAll() { | 291 public synchronized void cleanupAll() { |
| 255 List<IBinder> sessions = new ArrayList<>(mSessionParams.keySet()); | 292 List<IBinder> sessions = new ArrayList<>(mSessionParams.keySet()); |
| 256 for (IBinder session : sessions) cleanupSession(session); | 293 for (IBinder session : sessions) cleanupSession(session); |
| 257 } | 294 } |
| 258 | 295 |
| 259 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") | 296 @SuppressFBWarnings("CHROMIUM_SYNCHRONIZED_METHOD") |
| 260 private synchronized void cleanupSession(IBinder session) { | 297 private synchronized void cleanupSession(IBinder session) { |
| 261 SessionParams params = mSessionParams.get(session); | 298 SessionParams params = mSessionParams.get(session); |
| 262 if (params == null) return; | 299 if (params == null) return; |
| 263 mSessionParams.remove(session); | 300 mSessionParams.remove(session); |
| 301 mUidHasCalledWarmup.delete(params.uid); |
| 264 IBinder binder = params.callback.asBinder(); | 302 IBinder binder = params.callback.asBinder(); |
| 265 binder.unlinkToDeath(params.deathRecipient, 0); | 303 binder.unlinkToDeath(params.deathRecipient, 0); |
| 266 } | 304 } |
| 267 } | 305 } |
| OLD | NEW |