| Index: components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
|
| diff --git a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
|
| index 7bae2883221d5340a4ddbb23c19c9bf4865d978a..cd4b710f7c710b296620ba6ed0e150261833bf48 100644
|
| --- a/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
|
| +++ b/components/cronet/android/java/src/org/chromium/net/CronetUrlRequestContext.java
|
| @@ -12,6 +12,7 @@ import android.os.Looper;
|
| import android.os.Process;
|
| import android.util.Log;
|
|
|
| +import org.chromium.base.ObserverList;
|
| import org.chromium.base.VisibleForTesting;
|
| import org.chromium.base.annotations.CalledByNative;
|
| import org.chromium.base.annotations.JNINamespace;
|
| @@ -19,8 +20,11 @@ import org.chromium.base.annotations.NativeClassQualifiedName;
|
| import org.chromium.base.annotations.UsedByReflection;
|
|
|
| import java.util.concurrent.Executor;
|
| +import java.util.concurrent.RejectedExecutionException;
|
| import java.util.concurrent.atomic.AtomicInteger;
|
|
|
| +import javax.annotation.concurrent.GuardedBy;
|
| +
|
| /**
|
| * UrlRequestContext using Chromium HTTP stack implementation.
|
| */
|
| @@ -42,6 +46,22 @@ class CronetUrlRequestContext extends UrlRequestContext {
|
| private long mUrlRequestContextAdapter = 0;
|
| private Thread mNetworkThread;
|
|
|
| + private Executor mNetworkQualityExecutor;
|
| + private boolean mNetworkQualityEstimatorEnabled;
|
| +
|
| + /** Locks operations on network quality listeners, because listener
|
| + * addition and removal may occur on a different thread from notification.
|
| + */
|
| + private final Object mNetworkQualityLock = new Object();
|
| +
|
| + @GuardedBy("mNetworkQualityLock")
|
| + private final ObserverList<NetworkQualityRttListener> mRttListenerList =
|
| + new ObserverList<NetworkQualityRttListener>();
|
| +
|
| + @GuardedBy("mNetworkQualityLock")
|
| + private final ObserverList<NetworkQualityThroughputListener> mThroughputListenerList =
|
| + new ObserverList<NetworkQualityThroughputListener>();
|
| +
|
| @UsedByReflection("UrlRequestContext.java")
|
| public CronetUrlRequestContext(Context context,
|
| UrlRequestContextConfig config) {
|
| @@ -148,6 +168,94 @@ class CronetUrlRequestContext extends UrlRequestContext {
|
| }
|
| }
|
|
|
| + @Override
|
| + public void enableNetworkQualityEstimator(Executor executor) {
|
| + enableNetworkQualityEstimatorForTesting(false, false, executor);
|
| + }
|
| +
|
| + @VisibleForTesting
|
| + @Override
|
| + void enableNetworkQualityEstimatorForTesting(
|
| + boolean useLocalHostRequests, boolean useSmallerResponses, Executor executor) {
|
| + if (mNetworkQualityEstimatorEnabled) {
|
| + throw new IllegalStateException("Network quality estimator already enabled");
|
| + }
|
| + mNetworkQualityEstimatorEnabled = true;
|
| + if (executor == null) {
|
| + throw new NullPointerException("Network quality estimator requires an executor");
|
| + }
|
| + mNetworkQualityExecutor = executor;
|
| + synchronized (mLock) {
|
| + checkHaveAdapter();
|
| + nativeEnableNetworkQualityEstimator(
|
| + mUrlRequestContextAdapter, useLocalHostRequests, useSmallerResponses);
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void addRttListener(NetworkQualityRttListener listener) {
|
| + if (!mNetworkQualityEstimatorEnabled) {
|
| + throw new IllegalStateException("Network quality estimator must be enabled");
|
| + }
|
| + synchronized (mNetworkQualityLock) {
|
| + if (mRttListenerList.isEmpty()) {
|
| + synchronized (mLock) {
|
| + checkHaveAdapter();
|
| + nativeProvideRTTObservations(mUrlRequestContextAdapter, true);
|
| + }
|
| + }
|
| + mRttListenerList.addObserver(listener);
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void removeRttListener(NetworkQualityRttListener listener) {
|
| + if (!mNetworkQualityEstimatorEnabled) {
|
| + throw new IllegalStateException("Network quality estimator must be enabled");
|
| + }
|
| + synchronized (mNetworkQualityLock) {
|
| + mRttListenerList.removeObserver(listener);
|
| + if (mRttListenerList.isEmpty()) {
|
| + synchronized (mLock) {
|
| + checkHaveAdapter();
|
| + nativeProvideRTTObservations(mUrlRequestContextAdapter, false);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void addThroughputListener(NetworkQualityThroughputListener listener) {
|
| + if (!mNetworkQualityEstimatorEnabled) {
|
| + throw new IllegalStateException("Network quality estimator must be enabled");
|
| + }
|
| + synchronized (mNetworkQualityLock) {
|
| + if (mThroughputListenerList.isEmpty()) {
|
| + synchronized (mLock) {
|
| + checkHaveAdapter();
|
| + nativeProvideThroughputObservations(mUrlRequestContextAdapter, true);
|
| + }
|
| + }
|
| + mThroughputListenerList.addObserver(listener);
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void removeThroughputListener(NetworkQualityThroughputListener listener) {
|
| + if (!mNetworkQualityEstimatorEnabled) {
|
| + throw new IllegalStateException("Network quality estimator must be enabled");
|
| + }
|
| + synchronized (mNetworkQualityLock) {
|
| + mThroughputListenerList.removeObserver(listener);
|
| + if (mThroughputListenerList.isEmpty()) {
|
| + synchronized (mLock) {
|
| + checkHaveAdapter();
|
| + nativeProvideThroughputObservations(mUrlRequestContextAdapter, false);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| /**
|
| * Mark request as started to prevent shutdown when there are active
|
| * requests.
|
| @@ -209,6 +317,48 @@ class CronetUrlRequestContext extends UrlRequestContext {
|
| Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
| }
|
|
|
| + @SuppressWarnings("unused")
|
| + @CalledByNative
|
| + private void onRttObservation(final int rttMs, final long whenMs, final int source) {
|
| + Runnable task = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + synchronized (mNetworkQualityLock) {
|
| + for (NetworkQualityRttListener listener : mRttListenerList) {
|
| + listener.onRttObservation(rttMs, whenMs, source);
|
| + }
|
| + }
|
| + }
|
| + };
|
| + postObservationTaskToNetworkQualityExecutor(task);
|
| + }
|
| +
|
| + @SuppressWarnings("unused")
|
| + @CalledByNative
|
| + private void onThroughputObservation(
|
| + final int throughputKbps, final long whenMs, final int source) {
|
| + Runnable task = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + synchronized (mNetworkQualityLock) {
|
| + for (NetworkQualityThroughputListener listener : mThroughputListenerList) {
|
| + listener.onThroughputObservation(throughputKbps, whenMs, source);
|
| + }
|
| + }
|
| + }
|
| + };
|
| + postObservationTaskToNetworkQualityExecutor(task);
|
| + }
|
| +
|
| + void postObservationTaskToNetworkQualityExecutor(Runnable task) {
|
| + try {
|
| + mNetworkQualityExecutor.execute(task);
|
| + } catch (RejectedExecutionException failException) {
|
| + Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to executor",
|
| + failException);
|
| + }
|
| + }
|
| +
|
| // Native methods are implemented in cronet_url_request_context.cc.
|
| private static native long nativeCreateRequestContextAdapter(String config);
|
|
|
| @@ -226,4 +376,14 @@ class CronetUrlRequestContext extends UrlRequestContext {
|
|
|
| @NativeClassQualifiedName("CronetURLRequestContextAdapter")
|
| private native void nativeInitRequestContextOnMainThread(long nativePtr);
|
| +
|
| + @NativeClassQualifiedName("CronetURLRequestContextAdapter")
|
| + private native void nativeEnableNetworkQualityEstimator(
|
| + long nativePtr, boolean useLocalHostRequests, boolean useSmallerResponses);
|
| +
|
| + @NativeClassQualifiedName("CronetURLRequestContextAdapter")
|
| + private native void nativeProvideRTTObservations(long nativePtr, boolean should);
|
| +
|
| + @NativeClassQualifiedName("CronetURLRequestContextAdapter")
|
| + private native void nativeProvideThroughputObservations(long nativePtr, boolean should);
|
| }
|
|
|