Chromium Code Reviews| 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..e09793395cd2cfbeaf63ea6403e505b8c9d3cc8a 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,21 @@ class CronetUrlRequestContext extends UrlRequestContext { |
| private long mUrlRequestContextAdapter = 0; |
| private Thread mNetworkThread; |
| + private Executor mNetworkQualityExecutor; |
| + |
| + /** 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 +167,75 @@ class CronetUrlRequestContext extends UrlRequestContext { |
| } |
| } |
| + @Override |
| + public void enableNetworkQualityEstimator(Executor executor) { |
| + enableNetworkQualityEstimator(false, false, executor); |
| + } |
| + |
| + @VisibleForTesting |
| + @Override |
| + void enableNetworkQualityEstimator( |
| + boolean useLocalHostRequests, boolean useSmallerResponses, Executor executor) { |
| + mNetworkQualityExecutor = executor; |
|
mef
2015/10/01 19:02:28
This should check that executor != null and throw
bengr
2015/10/01 23:14:34
Done.
|
| + synchronized (mLock) { |
| + checkHaveAdapter(); |
| + nativeEnableNetworkQualityEstimator( |
| + mUrlRequestContextAdapter, useLocalHostRequests, useSmallerResponses); |
| + } |
| + } |
| + |
| + @Override |
| + public void addRttListener(NetworkQualityRttListener listener) { |
| + synchronized (mNetworkQualityLock) { |
| + if (mRttListenerList.isEmpty()) { |
| + synchronized (mLock) { |
| + checkHaveAdapter(); |
| + nativeProvideRTTObservations(mUrlRequestContextAdapter, true); |
| + } |
| + } |
| + mRttListenerList.addObserver(listener); |
| + } |
| + } |
| + |
| + @Override |
| + public void removeRttListener(NetworkQualityRttListener listener) { |
| + synchronized (mNetworkQualityLock) { |
| + mRttListenerList.removeObserver(listener); |
| + if (mRttListenerList.isEmpty()) { |
| + synchronized (mLock) { |
| + checkHaveAdapter(); |
| + nativeProvideRTTObservations(mUrlRequestContextAdapter, false); |
| + } |
| + } |
| + } |
| + } |
| + |
| + @Override |
| + public void addThroughputListener(NetworkQualityThroughputListener listener) { |
| + synchronized (mNetworkQualityLock) { |
| + if (mThroughputListenerList.isEmpty()) { |
| + synchronized (mLock) { |
| + checkHaveAdapter(); |
| + nativeProvideThroughputObservations(mUrlRequestContextAdapter, true); |
| + } |
| + } |
| + mThroughputListenerList.addObserver(listener); |
| + } |
| + } |
| + |
| + @Override |
| + public void removeThroughputListener(NetworkQualityThroughputListener listener) { |
| + 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 +297,46 @@ 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() { |
| + for (NetworkQualityRttListener listener : mRttListenerList) { |
|
mef
2015/10/01 19:02:28
BUG: need synchronized (mNetworkQualityLock) {
bengr
2015/10/01 23:14:34
Done.
|
| + listener.onRttObservation(rttMs, whenMs, source); |
| + } |
| + } |
| + }; |
| + postObservationTaskToExecutor(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); |
| + } |
| + } |
| + } |
| + }; |
| + postObservationTaskToExecutor(task); |
| + } |
| + |
| + void postObservationTaskToExecutor(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 +354,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); |
| } |