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); |
} |