| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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.net.impl; | 5 package org.chromium.net.impl; |
| 6 | 6 |
| 7 import android.os.SystemClock; | 7 import android.os.SystemClock; |
| 8 import android.support.annotation.Nullable; | 8 import android.support.annotation.Nullable; |
| 9 import android.util.Log; | 9 import android.util.Log; |
| 10 | 10 |
| 11 import org.chromium.base.VisibleForTesting; | 11 import org.chromium.base.VisibleForTesting; |
| 12 import org.chromium.base.annotations.CalledByNative; | 12 import org.chromium.base.annotations.CalledByNative; |
| 13 import org.chromium.base.annotations.JNIAdditionalImport; | 13 import org.chromium.base.annotations.JNIAdditionalImport; |
| 14 import org.chromium.base.annotations.JNINamespace; | 14 import org.chromium.base.annotations.JNINamespace; |
| 15 import org.chromium.base.annotations.NativeClassQualifiedName; | 15 import org.chromium.base.annotations.NativeClassQualifiedName; |
| 16 import org.chromium.net.InlineExecutionProhibitedException; |
| 16 import org.chromium.net.Preconditions; | 17 import org.chromium.net.Preconditions; |
| 17 import org.chromium.net.QuicException; | 18 import org.chromium.net.QuicException; |
| 18 import org.chromium.net.RequestFinishedInfo; | 19 import org.chromium.net.RequestFinishedInfo; |
| 19 import org.chromium.net.RequestPriority; | 20 import org.chromium.net.RequestPriority; |
| 20 import org.chromium.net.UploadDataProvider; | 21 import org.chromium.net.UploadDataProvider; |
| 21 import org.chromium.net.UrlRequest; | 22 import org.chromium.net.UrlRequest; |
| 22 import org.chromium.net.UrlRequestException; | 23 import org.chromium.net.UrlRequestException; |
| 23 import org.chromium.net.UrlResponseInfo; | 24 import org.chromium.net.UrlResponseInfo; |
| 24 | 25 |
| 25 import java.nio.ByteBuffer; | 26 import java.nio.ByteBuffer; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 42 * native tasks to native network thread. Because Cancel could be called from | 43 * native tasks to native network thread. Because Cancel could be called from |
| 43 * any thread it is protected by mUrlRequestAdapterLock. | 44 * any thread it is protected by mUrlRequestAdapterLock. |
| 44 */ | 45 */ |
| 45 @JNINamespace("cronet") | 46 @JNINamespace("cronet") |
| 46 // Qualifies UrlRequest.StatusListener which is used in onStatus, a JNI method. | 47 // Qualifies UrlRequest.StatusListener which is used in onStatus, a JNI method. |
| 47 @JNIAdditionalImport(UrlRequest.class) | 48 @JNIAdditionalImport(UrlRequest.class) |
| 48 @VisibleForTesting | 49 @VisibleForTesting |
| 49 public final class CronetUrlRequest implements UrlRequest { | 50 public final class CronetUrlRequest implements UrlRequest { |
| 50 private static final RequestFinishedInfo.Metrics EMPTY_METRICS = | 51 private static final RequestFinishedInfo.Metrics EMPTY_METRICS = |
| 51 new RequestFinishedInfo.Metrics(null, null, null, null); | 52 new RequestFinishedInfo.Metrics(null, null, null, null); |
| 53 private final boolean mAllowDirectExecutor; |
| 52 | 54 |
| 53 /* Native adapter object, owned by UrlRequest. */ | 55 /* Native adapter object, owned by UrlRequest. */ |
| 54 @GuardedBy("mUrlRequestAdapterLock") | 56 @GuardedBy("mUrlRequestAdapterLock") |
| 55 private long mUrlRequestAdapter; | 57 private long mUrlRequestAdapter; |
| 56 | 58 |
| 57 @GuardedBy("mUrlRequestAdapterLock") | 59 @GuardedBy("mUrlRequestAdapterLock") |
| 58 private boolean mStarted = false; | 60 private boolean mStarted = false; |
| 59 @GuardedBy("mUrlRequestAdapterLock") | 61 @GuardedBy("mUrlRequestAdapterLock") |
| 62 private boolean mFailed = false; |
| 63 @GuardedBy("mUrlRequestAdapterLock") |
| 60 private boolean mWaitingOnRedirect = false; | 64 private boolean mWaitingOnRedirect = false; |
| 61 @GuardedBy("mUrlRequestAdapterLock") | 65 @GuardedBy("mUrlRequestAdapterLock") |
| 62 private boolean mWaitingOnRead = false; | 66 private boolean mWaitingOnRead = false; |
| 63 @GuardedBy("mUrlRequestAdapterLock") | 67 @GuardedBy("mUrlRequestAdapterLock") |
| 64 @Nullable | 68 @Nullable |
| 65 private final UrlRequestMetricsAccumulator mRequestMetricsAccumulator; | 69 private final UrlRequestMetricsAccumulator mRequestMetricsAccumulator; |
| 66 | 70 |
| 67 /* | 71 /* |
| 68 * Synchronize access to mUrlRequestAdapter, mStarted, mWaitingOnRedirect, | 72 * Synchronize access to mUrlRequestAdapter, mStarted, mWaitingOnRedirect, |
| 69 * and mWaitingOnRead. | 73 * and mWaitingOnRead. |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 private Runnable mOnDestroyedCallbackForTesting; | 106 private Runnable mOnDestroyedCallbackForTesting; |
| 103 | 107 |
| 104 private static final class HeadersList extends ArrayList<Map.Entry<String, S
tring>> {} | 108 private static final class HeadersList extends ArrayList<Map.Entry<String, S
tring>> {} |
| 105 | 109 |
| 106 private final class OnReadCompletedRunnable implements Runnable { | 110 private final class OnReadCompletedRunnable implements Runnable { |
| 107 // Buffer passed back from current invocation of onReadCompleted. | 111 // Buffer passed back from current invocation of onReadCompleted. |
| 108 ByteBuffer mByteBuffer; | 112 ByteBuffer mByteBuffer; |
| 109 | 113 |
| 110 @Override | 114 @Override |
| 111 public void run() { | 115 public void run() { |
| 116 checkCallingThread(); |
| 112 // Null out mByteBuffer, to pass buffer ownership to callback or rel
ease if done. | 117 // Null out mByteBuffer, to pass buffer ownership to callback or rel
ease if done. |
| 113 ByteBuffer buffer = mByteBuffer; | 118 ByteBuffer buffer = mByteBuffer; |
| 114 mByteBuffer = null; | 119 mByteBuffer = null; |
| 115 | 120 |
| 116 try { | 121 try { |
| 117 synchronized (mUrlRequestAdapterLock) { | 122 synchronized (mUrlRequestAdapterLock) { |
| 118 if (isDoneLocked()) { | 123 if (isDoneLocked()) { |
| 119 return; | 124 return; |
| 120 } | 125 } |
| 121 mWaitingOnRead = true; | 126 mWaitingOnRead = true; |
| 122 } | 127 } |
| 123 mCallback.onReadCompleted(CronetUrlRequest.this, mResponseInfo,
buffer); | 128 mCallback.onReadCompleted(CronetUrlRequest.this, mResponseInfo,
buffer); |
| 124 } catch (Exception e) { | 129 } catch (Exception e) { |
| 125 onCallbackException(e); | 130 onCallbackException(e); |
| 126 } | 131 } |
| 127 } | 132 } |
| 128 } | 133 } |
| 129 | 134 |
| 130 CronetUrlRequest(CronetUrlRequestContext requestContext, String url, int pri
ority, | 135 CronetUrlRequest(CronetUrlRequestContext requestContext, String url, int pri
ority, |
| 131 UrlRequest.Callback callback, Executor executor, Collection<Object>
requestAnnotations, | 136 UrlRequest.Callback callback, Executor executor, Collection<Object>
requestAnnotations, |
| 132 boolean metricsCollectionEnabled, boolean disableCache, | 137 boolean metricsCollectionEnabled, boolean disableCache, |
| 133 boolean disableConnectionMigration) { | 138 boolean disableConnectionMigration, boolean allowDirectExecutor) { |
| 134 if (url == null) { | 139 if (url == null) { |
| 135 throw new NullPointerException("URL is required"); | 140 throw new NullPointerException("URL is required"); |
| 136 } | 141 } |
| 137 if (callback == null) { | 142 if (callback == null) { |
| 138 throw new NullPointerException("Listener is required"); | 143 throw new NullPointerException("Listener is required"); |
| 139 } | 144 } |
| 140 if (executor == null) { | 145 if (executor == null) { |
| 141 throw new NullPointerException("Executor is required"); | 146 throw new NullPointerException("Executor is required"); |
| 142 } | 147 } |
| 143 if (requestAnnotations == null) { | 148 if (requestAnnotations == null) { |
| 144 throw new NullPointerException("requestAnnotations is required"); | 149 throw new NullPointerException("requestAnnotations is required"); |
| 145 } | 150 } |
| 146 | 151 |
| 152 mAllowDirectExecutor = allowDirectExecutor; |
| 147 mRequestContext = requestContext; | 153 mRequestContext = requestContext; |
| 148 mInitialUrl = url; | 154 mInitialUrl = url; |
| 149 mUrlChain.add(url); | 155 mUrlChain.add(url); |
| 150 mPriority = convertRequestPriority(priority); | 156 mPriority = convertRequestPriority(priority); |
| 151 mCallback = callback; | 157 mCallback = callback; |
| 152 mExecutor = executor; | 158 mExecutor = executor; |
| 153 mRequestAnnotations = requestAnnotations; | 159 mRequestAnnotations = requestAnnotations; |
| 154 mRequestMetricsAccumulator = | 160 mRequestMetricsAccumulator = |
| 155 metricsCollectionEnabled ? new UrlRequestMetricsAccumulator() :
null; | 161 metricsCollectionEnabled ? new UrlRequestMetricsAccumulator() :
null; |
| 156 mDisableCache = disableCache; | 162 mDisableCache = disableCache; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 throw new IllegalArgumentException( | 222 throw new IllegalArgumentException( |
| 217 "Invalid header " + header.getKey() + "=" + head
er.getValue()); | 223 "Invalid header " + header.getKey() + "=" + head
er.getValue()); |
| 218 } | 224 } |
| 219 } | 225 } |
| 220 if (mUploadDataStream != null) { | 226 if (mUploadDataStream != null) { |
| 221 if (!hasContentType) { | 227 if (!hasContentType) { |
| 222 throw new IllegalArgumentException( | 228 throw new IllegalArgumentException( |
| 223 "Requests with upload data must have a Content-T
ype."); | 229 "Requests with upload data must have a Content-T
ype."); |
| 224 } | 230 } |
| 225 mStarted = true; | 231 mStarted = true; |
| 226 mUploadDataStream.postTaskToExecutor(new Runnable() { | 232 mUploadDataStream.initializeWithRequest(CronetUrlRequest.thi
s, new Runnable() { |
| 227 @Override | 233 @Override |
| 228 public void run() { | 234 public void run() { |
| 229 mUploadDataStream.initializeWithRequest(CronetUrlReq
uest.this); | |
| 230 synchronized (mUrlRequestAdapterLock) { | 235 synchronized (mUrlRequestAdapterLock) { |
| 231 if (isDoneLocked()) { | 236 if (isDoneLocked()) { |
| 232 return; | 237 return; |
| 233 } | 238 } |
| 234 mUploadDataStream.attachNativeAdapterToRequest(m
UrlRequestAdapter); | 239 mUploadDataStream.attachNativeAdapterToRequest(m
UrlRequestAdapter); |
| 235 startInternalLocked(); | 240 startInternalLocked(); |
| 236 } | 241 } |
| 237 } | 242 } |
| 238 }); | 243 }); |
| 239 return; | 244 return; |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 362 * and other tasks that should not be executed on network thread. | 367 * and other tasks that should not be executed on network thread. |
| 363 */ | 368 */ |
| 364 private void postTaskToExecutor(Runnable task) { | 369 private void postTaskToExecutor(Runnable task) { |
| 365 try { | 370 try { |
| 366 mExecutor.execute(task); | 371 mExecutor.execute(task); |
| 367 } catch (RejectedExecutionException failException) { | 372 } catch (RejectedExecutionException failException) { |
| 368 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex
ecutor", | 373 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex
ecutor", |
| 369 failException); | 374 failException); |
| 370 // If posting a task throws an exception, then there is no choice | 375 // If posting a task throws an exception, then there is no choice |
| 371 // but to destroy the request without invoking the callback. | 376 // but to destroy the request without invoking the callback. |
| 377 failWithException( |
| 378 new UrlRequestException("Exception posting task to executor"
, failException)); |
| 372 destroyRequestAdapter(false); | 379 destroyRequestAdapter(false); |
| 373 } | 380 } |
| 374 } | 381 } |
| 375 | 382 |
| 376 private static int convertRequestPriority(int priority) { | 383 private static int convertRequestPriority(int priority) { |
| 377 switch (priority) { | 384 switch (priority) { |
| 378 case Builder.REQUEST_PRIORITY_IDLE: | 385 case Builder.REQUEST_PRIORITY_IDLE: |
| 379 return RequestPriority.IDLE; | 386 return RequestPriority.IDLE; |
| 380 case Builder.REQUEST_PRIORITY_LOWEST: | 387 case Builder.REQUEST_PRIORITY_LOWEST: |
| 381 return RequestPriority.LOWEST; | 388 return RequestPriority.LOWEST; |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 UrlRequestException uploadError = | 466 UrlRequestException uploadError = |
| 460 new UrlRequestException("Exception received from UploadDataProvi
der", e); | 467 new UrlRequestException("Exception received from UploadDataProvi
der", e); |
| 461 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in upload method", e); | 468 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in upload method", e); |
| 462 failWithException(uploadError); | 469 failWithException(uploadError); |
| 463 } | 470 } |
| 464 | 471 |
| 465 /** | 472 /** |
| 466 * Fails the request with an exception. Can be called on any thread. | 473 * Fails the request with an exception. Can be called on any thread. |
| 467 */ | 474 */ |
| 468 private void failWithException(final UrlRequestException exception) { | 475 private void failWithException(final UrlRequestException exception) { |
| 476 synchronized (mUrlRequestAdapterLock) { |
| 477 if (mFailed) { |
| 478 return; |
| 479 } else { |
| 480 mFailed = true; |
| 481 } |
| 482 } |
| 469 Runnable task = new Runnable() { | 483 Runnable task = new Runnable() { |
| 470 @Override | 484 @Override |
| 471 public void run() { | 485 public void run() { |
| 472 synchronized (mUrlRequestAdapterLock) { | 486 synchronized (mUrlRequestAdapterLock) { |
| 473 if (isDoneLocked()) { | 487 if (isDoneLocked()) { |
| 474 return; | 488 return; |
| 475 } | 489 } |
| 476 destroyRequestAdapter(false); | 490 destroyRequestAdapter(false); |
| 477 } | 491 } |
| 478 try { | 492 try { |
| 479 mCallback.onFailed(CronetUrlRequest.this, mResponseInfo, exc
eption); | 493 mCallback.onFailed(CronetUrlRequest.this, mResponseInfo, exc
eption); |
| 480 } catch (Exception e) { | 494 } catch (Exception e) { |
| 481 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onError
method", e); | 495 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onError
method", e); |
| 482 } | 496 } |
| 483 } | 497 } |
| 484 }; | 498 }; |
| 485 postTaskToExecutor(task); | 499 try { |
| 500 mExecutor.execute(task); |
| 501 } catch (RejectedExecutionException e) { |
| 502 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex
ecutor", e); |
| 503 } |
| 486 } | 504 } |
| 487 | 505 |
| 488 //////////////////////////////////////////////// | 506 //////////////////////////////////////////////// |
| 489 // Private methods called by the native code. | 507 // Private methods called by the native code. |
| 490 // Always called on network thread. | 508 // Always called on network thread. |
| 491 //////////////////////////////////////////////// | 509 //////////////////////////////////////////////// |
| 492 | 510 |
| 493 /** | 511 /** |
| 494 * Called before following redirects. The redirect will only be followed if | 512 * Called before following redirects. The redirect will only be followed if |
| 495 * {@link #followRedirect()} is called. If the redirect response has a body,
it will be ignored. | 513 * {@link #followRedirect()} is called. If the redirect response has a body,
it will be ignored. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 510 httpStatusText, headers, wasCached, negotiatedProtocol, proxySer
ver); | 528 httpStatusText, headers, wasCached, negotiatedProtocol, proxySer
ver); |
| 511 mReceivedBytesCountFromRedirects += receivedBytesCount; | 529 mReceivedBytesCountFromRedirects += receivedBytesCount; |
| 512 responseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects); | 530 responseInfo.setReceivedBytesCount(mReceivedBytesCountFromRedirects); |
| 513 | 531 |
| 514 // Have to do this after creating responseInfo. | 532 // Have to do this after creating responseInfo. |
| 515 mUrlChain.add(newLocation); | 533 mUrlChain.add(newLocation); |
| 516 | 534 |
| 517 Runnable task = new Runnable() { | 535 Runnable task = new Runnable() { |
| 518 @Override | 536 @Override |
| 519 public void run() { | 537 public void run() { |
| 538 checkCallingThread(); |
| 520 synchronized (mUrlRequestAdapterLock) { | 539 synchronized (mUrlRequestAdapterLock) { |
| 521 if (isDoneLocked()) { | 540 if (isDoneLocked()) { |
| 522 return; | 541 return; |
| 523 } | 542 } |
| 524 mWaitingOnRedirect = true; | 543 mWaitingOnRedirect = true; |
| 525 } | 544 } |
| 526 | 545 |
| 527 try { | 546 try { |
| 528 mCallback.onRedirectReceived(CronetUrlRequest.this, response
Info, newLocation); | 547 mCallback.onRedirectReceived(CronetUrlRequest.this, response
Info, newLocation); |
| 529 } catch (Exception e) { | 548 } catch (Exception e) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 540 */ | 559 */ |
| 541 @SuppressWarnings("unused") | 560 @SuppressWarnings("unused") |
| 542 @CalledByNative | 561 @CalledByNative |
| 543 private void onResponseStarted(int httpStatusCode, String httpStatusText, St
ring[] headers, | 562 private void onResponseStarted(int httpStatusCode, String httpStatusText, St
ring[] headers, |
| 544 boolean wasCached, String negotiatedProtocol, String proxyServer) { | 563 boolean wasCached, String negotiatedProtocol, String proxyServer) { |
| 545 mResponseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode, httpS
tatusText, headers, | 564 mResponseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode, httpS
tatusText, headers, |
| 546 wasCached, negotiatedProtocol, proxyServer); | 565 wasCached, negotiatedProtocol, proxyServer); |
| 547 Runnable task = new Runnable() { | 566 Runnable task = new Runnable() { |
| 548 @Override | 567 @Override |
| 549 public void run() { | 568 public void run() { |
| 569 checkCallingThread(); |
| 550 synchronized (mUrlRequestAdapterLock) { | 570 synchronized (mUrlRequestAdapterLock) { |
| 551 if (isDoneLocked()) { | 571 if (isDoneLocked()) { |
| 552 return; | 572 return; |
| 553 } | 573 } |
| 554 if (mRequestMetricsAccumulator != null) { | 574 if (mRequestMetricsAccumulator != null) { |
| 555 mRequestMetricsAccumulator.onResponseStarted(); | 575 mRequestMetricsAccumulator.onResponseStarted(); |
| 556 } | 576 } |
| 557 mWaitingOnRead = true; | 577 mWaitingOnRead = true; |
| 558 } | 578 } |
| 559 | 579 |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 729 } | 749 } |
| 730 } | 750 } |
| 731 | 751 |
| 732 private void onResponseStarted() { | 752 private void onResponseStarted() { |
| 733 if (mRequestStartTime != null && mTtfbMs == null) { | 753 if (mRequestStartTime != null && mTtfbMs == null) { |
| 734 mTtfbMs = SystemClock.elapsedRealtime() - mRequestStartTime; | 754 mTtfbMs = SystemClock.elapsedRealtime() - mRequestStartTime; |
| 735 } | 755 } |
| 736 } | 756 } |
| 737 } | 757 } |
| 738 | 758 |
| 759 /** Enforces prohibition of direct execution. */ |
| 760 private void checkCallingThread() { |
| 761 if (!mAllowDirectExecutor && mRequestContext.isNativeThread(Thread.curre
ntThread())) { |
| 762 throw new InlineExecutionProhibitedException(); |
| 763 } |
| 764 } |
| 765 |
| 739 // Native methods are implemented in cronet_url_request_adapter.cc. | 766 // Native methods are implemented in cronet_url_request_adapter.cc. |
| 740 | 767 |
| 741 private native long nativeCreateRequestAdapter(long urlRequestContextAdapter
, String url, | 768 private native long nativeCreateRequestAdapter(long urlRequestContextAdapter
, String url, |
| 742 int priority, boolean disableCache, boolean disableConnectionMigrati
on); | 769 int priority, boolean disableCache, boolean disableConnectionMigrati
on); |
| 743 | 770 |
| 744 @NativeClassQualifiedName("CronetURLRequestAdapter") | 771 @NativeClassQualifiedName("CronetURLRequestAdapter") |
| 745 private native boolean nativeSetHttpMethod(long nativePtr, String method); | 772 private native boolean nativeSetHttpMethod(long nativePtr, String method); |
| 746 | 773 |
| 747 @NativeClassQualifiedName("CronetURLRequestAdapter") | 774 @NativeClassQualifiedName("CronetURLRequestAdapter") |
| 748 private native boolean nativeAddRequestHeader(long nativePtr, String name, S
tring value); | 775 private native boolean nativeAddRequestHeader(long nativePtr, String name, S
tring value); |
| 749 | 776 |
| 750 @NativeClassQualifiedName("CronetURLRequestAdapter") | 777 @NativeClassQualifiedName("CronetURLRequestAdapter") |
| 751 private native void nativeStart(long nativePtr); | 778 private native void nativeStart(long nativePtr); |
| 752 | 779 |
| 753 @NativeClassQualifiedName("CronetURLRequestAdapter") | 780 @NativeClassQualifiedName("CronetURLRequestAdapter") |
| 754 private native void nativeFollowDeferredRedirect(long nativePtr); | 781 private native void nativeFollowDeferredRedirect(long nativePtr); |
| 755 | 782 |
| 756 @NativeClassQualifiedName("CronetURLRequestAdapter") | 783 @NativeClassQualifiedName("CronetURLRequestAdapter") |
| 757 private native boolean nativeReadData( | 784 private native boolean nativeReadData( |
| 758 long nativePtr, ByteBuffer byteBuffer, int position, int capacity); | 785 long nativePtr, ByteBuffer byteBuffer, int position, int capacity); |
| 759 | 786 |
| 760 @NativeClassQualifiedName("CronetURLRequestAdapter") | 787 @NativeClassQualifiedName("CronetURLRequestAdapter") |
| 761 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled); | 788 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled); |
| 762 | 789 |
| 763 @NativeClassQualifiedName("CronetURLRequestAdapter") | 790 @NativeClassQualifiedName("CronetURLRequestAdapter") |
| 764 private native void nativeGetStatus(long nativePtr, UrlRequest.StatusListene
r listener); | 791 private native void nativeGetStatus(long nativePtr, UrlRequest.StatusListene
r listener); |
| 765 } | 792 } |
| OLD | NEW |