Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(552)

Side by Side Diff: components/cronet/android/java/src/org/chromium/net/CronetBidirectionalStream.java

Issue 1412243012: Initial implementation of CronetBidirectionalStream. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sync Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.net;
6
7 import android.util.Log;
xunjieli 2015/12/18 19:11:12 we should avoid using android.util.Log in new file
mef 2015/12/29 20:36:54 Done.
8
9 import org.chromium.base.VisibleForTesting;
10 import org.chromium.base.annotations.CalledByNative;
11 import org.chromium.base.annotations.JNINamespace;
12 import org.chromium.base.annotations.NativeClassQualifiedName;
13
14 import java.nio.ByteBuffer;
15 import java.util.AbstractMap;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.concurrent.Executor;
20 import java.util.concurrent.RejectedExecutionException;
21
22 import javax.annotation.concurrent.GuardedBy;
23
24 /**
25 * BidirectionalStream implementation using Chromium network stack.
26 * All @CallByNative methods are called on native network thread
27 * and post tasks with callback calls onto Executor. Upon return from callback n ative
28 * stream is called on executor thread and posts native tasks to native network thread.
29 */
30 @JNINamespace("cronet")
31 class CronetBidirectionalStream extends BidirectionalStream {
32 /**
33 * States of BidirectionalStream are tracked in mStreamState and mWriteState .
34 * The write state is separated out as it changes independently of the strea m state.
35 * There is one initial state - STATE_NOT_STARTED. There is one final state - STATE_SUCCESS,
36 * reached after STATE_READING_DONE and STATE_WRITING_DONE. There are 2 exce ption states -
37 * STATE_CANCELED and STATE_ERROR, which can be reached from any other state except
38 * STATE_SUCCESS.
39 */
40 /* Initial state, stream not started. */
41 private static final int STATE_NOT_STARTED = 0;
42 /* Stream started, request headers are sent. */
43 private static final int STATE_STARTED = 1;
44 /* Waiting for {@code read()} to be called. */
45 private static final int STATE_WAITING_ON_READ = 2;
46 /* Reading from the remote, {@code onReadCompleted()} callback will be calle d when done. */
47 private static final int STATE_READING = 3;
48 /* There is no more data to read and stream is half-closed by the remote sid e. */
49 private static final int STATE_READING_DONE = 4;
50 /* Stream is canceled. */
51 private static final int STATE_CANCELED = 5;
52 /* Error has occured, stream is closed. */
53 private static final int STATE_ERROR = 6;
54 /* Reading and writing is done, and the stream is closed successfully. */
55 private static final int STATE_SUCCESS = 7;
56 /* Waiting for {@code write()} to be called. */
57 private static final int STATE_WAITING_ON_WRITE = 10;
58 /* Writing to the remote, {@code onWriteCompleted()} callback will be called when done. */
59 private static final int STATE_WRITING = 11;
60 /* Writing the last frame, so {@code STATE_WRITING_DONE} will be set upon co mpletion. */
61 private static final int STATE_WRITING_END_OF_STREAM = 12;
62 /* There is no more data to write and stream is half-closed by the local sid e. */
63 private static final int STATE_WRITING_DONE = 13;
64
65 /*
66 * Synchronize access to mNativeStream, mStreamState and mWriteState.
67 */
68 private final Object mNativeStreamLock = new Object();
69 private final CronetUrlRequestContext mRequestContext;
70 private final Executor mExecutor;
71 private final Callback mCallback;
72 private final String mInitialUrl;
73 private final String mInitialMethod;
74 private final ArrayList<Map.Entry<String, String>> mRequestHeaders;
75
76 /* Native BidirectionalStream object, owned by CronetBidirectionalStream. */
77 @GuardedBy("mNativeStreamLock") private long mNativeStream;
78
79 /**
80 * Stream state is tracking stream and reading flow.
81 * NOT_STARTED -> STARTED -> WAITING_ON_READ -> READING -> WAITING_ON_READ - >
82 * READING -> READING_DONE -> SUCCESS
83 */
84 @GuardedBy("mNativeStreamLock") private int mStreamState = STATE_NOT_STARTED ;
xunjieli 2015/12/18 19:49:25 nit: mReadState?
mef 2015/12/29 20:36:54 I'm not sure. This is both read and common stream
85
86 /**
87 * Write state is tracking writing flow.
88 * NOT_STARTED -> WAITING_ON_WRITE -> WRITING -> WAITING_ON_WRITE ->
89 * WRITING_END_OF_STREAM -> WRITING_DONE -> SUCCESS
90 */
91 @GuardedBy("mNativeStreamLock") private int mWriteState = STATE_NOT_STARTED;
92
93 private UrlResponseInfo mResponseInfo;
94
95 /*
96 * OnReadCompleted callback is repeatedly invoked when each read is complete d, so it
97 * is cached as a member variable.
98 */
99 private OnReadCompletedRunnable mOnReadCompletedTask;
100
101 /*
102 * OnWriteCompleted callback is repeatedly invoked when each write is comple ted, so it
103 * is cached as a member variable.
104 */
105 private OnWriteCompletedRunnable mOnWriteCompletedTask;
106
107 private Runnable mOnDestroyedCallbackForTests;
xunjieli 2015/12/18 19:11:11 nit: maybe using ForTesting. I searched in cronet/
mef 2015/12/29 20:36:54 Done.
108
109 private final class OnReadCompletedRunnable implements Runnable {
110 ByteBuffer mByteBuffer;
111 boolean mEndOfStream;
xunjieli 2015/12/18 19:11:12 nit: need documentation on these two fields.
mef 2015/12/29 20:36:54 Done.
112
113 @Override
114 public void run() {
115 if (isDone()) {
116 return;
117 }
118 try {
119 synchronized (mNativeStreamLock) {
120 if (mNativeStream == 0) {
121 return;
122 }
123 if (mEndOfStream) {
124 mStreamState = STATE_READING_DONE;
125 if (maybeSucceeded()) return;
126 } else {
127 mStreamState = STATE_WAITING_ON_READ;
128 }
129 }
130 // Null out mByteBuffer, out of paranoia. Has to be done before
131 // mCallback call, to avoid any race when there are multiple
132 // executor threads.
133 ByteBuffer buffer = mByteBuffer;
134 mByteBuffer = null;
135 mCallback.onReadCompleted(CronetBidirectionalStream.this, mRespo nseInfo, buffer);
136 } catch (Exception e) {
137 onCallbackException(e);
138 }
139 }
140 }
141
142 private final class OnWriteCompletedRunnable implements Runnable {
143 ByteBuffer mByteBuffer;
xunjieli 2015/12/18 19:11:12 nit: need doc on this mByteBuffer field.
mef 2015/12/29 20:36:54 Done.
144
145 @Override
146 public void run() {
147 if (isDone()) {
148 return;
149 }
150 try {
151 synchronized (mNativeStreamLock) {
152 if (mNativeStream == 0) {
153 return;
154 }
155 if (mWriteState == STATE_WRITING_END_OF_STREAM) {
156 mWriteState = STATE_WRITING_DONE;
157 if (maybeSucceeded()) return;
158 } else {
159 mWriteState = STATE_WAITING_ON_WRITE;
160 }
161 }
162 // Null out mByteBuffer, out of paranoia. Has to be done before
163 // mCallback call, to avoid any race when there are multiple
164 // executor threads.
165 ByteBuffer buffer = mByteBuffer;
166 mByteBuffer = null;
167 mCallback.onWriteCompleted(CronetBidirectionalStream.this, mResp onseInfo, buffer);
168 } catch (Exception e) {
169 onCallbackException(e);
170 }
171 }
172 }
173
174 @GuardedBy("nativeStreamLock")
175 private boolean maybeSucceeded() {
176 if (mStreamState != STATE_READING_DONE || mWriteState != STATE_WRITING_D ONE) {
177 return false;
178 }
179
180 mStreamState = STATE_SUCCESS;
181 Runnable task = new Runnable() {
182 public void run() {
183 synchronized (mNativeStreamLock) {
184 if (isDone()) {
185 return;
186 }
187 // Destroy native stream first, so request context could be shut
188 // down from the listener.
189 destroyNativeStream(false);
190 }
191 try {
192 mCallback.onSucceeded(CronetBidirectionalStream.this, mRespo nseInfo);
193 } catch (Exception e) {
194 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onSucce eded method", e);
195 }
196 }
197 };
198 postTaskToExecutor(task);
199 return true;
200 }
201
202 private static boolean doesMethodAllowWriteData(String methodName) {
203 return !methodName.equals("GET") && !methodName.equals("HEAD");
204 }
205
206 CronetBidirectionalStream(CronetUrlRequestContext requestContext, long urlRe questContextAdapter,
xunjieli 2015/12/18 19:11:11 nit: move constructor to the top. At least before
mef 2015/12/29 20:36:54 Done.
207 String url, Callback callback, Executor executor, String httpMethod,
208 List<Map.Entry<String, String>> requestHeaders) {
209 mRequestContext = requestContext;
210 mInitialUrl = url;
211 mCallback = callback;
212 mExecutor = executor;
213 mInitialMethod = httpMethod;
214 mRequestHeaders = new ArrayList<Map.Entry<String, String>>(requestHeader s);
xunjieli 2015/12/18 19:11:11 Is this a deep copy?
mef 2015/12/29 20:36:54 It constructs a list containing elements of passed
215 }
216
217 @Override
218 public void start() {
219 synchronized (mNativeStreamLock) {
220 if (mStreamState != STATE_NOT_STARTED) {
221 throw new IllegalStateException("Stream is already started.");
222 }
223 try {
224 mNativeStream = nativeCreateBidirectionalStream(
225 mRequestContext.getUrlRequestContextAdapter());
226 mRequestContext.onRequestStarted();
227 String headers[] = stringsFromHeaderList(mRequestHeaders);
228 // Non-zero startResult means an argument error.
229 int startResult = nativeStart(mNativeStream, mInitialUrl, mIniti alMethod, headers,
230 !doesMethodAllowWriteData(mInitialMethod));
231 if (startResult == -1) {
232 throw new IllegalArgumentException("Invalid http method " + mInitialMethod);
233 }
234 if (startResult > 0) {
235 int headerPos = startResult - 1;
236 throw new IllegalArgumentException(
237 "Invalid header " + headers[headerPos] + "=" + heade rs[headerPos + 1]);
238 }
239 mStreamState = STATE_STARTED;
240 } catch (RuntimeException e) {
241 // If there's an exception, cleanup and then throw the
242 // exception to the caller.
243 destroyNativeStream(false);
244 throw e;
245 }
246 }
247 }
248
249 @Override
250 public void read(ByteBuffer buffer) {
251 synchronized (mNativeStreamLock) {
252 if (!buffer.hasRemaining()) {
253 throw new IllegalArgumentException("ByteBuffer is already full." );
254 }
255 if (mStreamState != STATE_WAITING_ON_READ) {
256 throw new IllegalStateException("Unexpected read attempt.");
257 }
258 if (isDone()) {
259 return;
260 }
261 mStreamState = STATE_READING;
262 if (!nativeReadData(mNativeStream, buffer, buffer.position(), buffer .limit())) {
263 // Still waiting on read. This is just to have consistent
264 // behavior with the other error cases.
265 mStreamState = STATE_WAITING_ON_READ;
266 // Since accessing byteBuffer's memory failed, it's presumably
267 // not a direct ByteBuffer.
268 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
269 }
270 }
271 }
272
273 @Override
274 public void write(ByteBuffer buffer, boolean endOfStream) {
275 synchronized (mNativeStreamLock) {
276 if (!buffer.hasRemaining() && !endOfStream) {
277 throw new IllegalArgumentException("Empty buffer before end of s tream.");
278 }
279 if (mWriteState != STATE_WAITING_ON_WRITE) {
280 throw new IllegalStateException("Unexpected write attempt.");
281 }
282 if (isDone()) {
283 return;
284 }
285 mWriteState = endOfStream ? STATE_WRITING_END_OF_STREAM : STATE_WRIT ING;
286 if (!nativeWriteData(
287 mNativeStream, buffer, buffer.position(), buffer.limit() , endOfStream)) {
288 // Still waiting on write. This is just to have consistent
289 // behavior with the other error cases.
290 mWriteState = STATE_WAITING_ON_WRITE;
291 // Since accessing byteBuffer's memory failed, it's presumably
292 // not a direct ByteBuffer.
293 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
294 }
295 }
296 }
297
298 @Override
299 public void ping(PingCallback callback, Executor executor) {
300 // TODO(mef): May be last thing to be implemented on Android.
xunjieli 2015/12/18 19:11:11 nit: throw UnsupportedOperation with a not yet sup
mef 2015/12/29 20:36:55 Done.
301 }
302
303 @Override
304 public void windowUpdate(int windowSizeIncrement) {
305 // TODO(mef): Understand the needs and semantics of this method.
306 }
307
308 /**
309 * Cancels the stream. Can be called at any time after {@link #start}.
310 * {@link Callback#onCanceled} will be invoked when cancelation
311 * is complete and no further callback methods will be invoked. If the
312 * stream has completed or has not started, calling {@code cancel()} has no
313 * effect and {@code onCanceled} will not be invoked. If the
314 * {@link Executor} passed in during {@code BidirectionalStream} constructio n runs
315 * tasks on a single thread, and {@code cancel()} is called on that thread,
316 * no listener methods (besides {@code onCanceled()}) will be invoked after
317 * {@code cancel()} is called. Otherwise, at most one callback method may be
318 * invoked after {@code cancel()} has completed.
319 */
320 public void cancel() {
xunjieli 2015/12/18 19:11:11 nit: add @override, and remove documentation.
mef 2015/12/29 20:36:54 Done.
321 synchronized (mNativeStreamLock) {
322 if (isDone() || mStreamState == STATE_NOT_STARTED) {
323 return;
324 }
325 mStreamState = STATE_CANCELED;
326 destroyNativeStream(true);
327 }
328 }
329
330 @Override
331 public boolean isDone() {
332 synchronized (mNativeStreamLock) {
333 return mStreamState != STATE_NOT_STARTED && mNativeStream == 0;
334 }
335 }
336
337 @SuppressWarnings("unused")
338 @CalledByNative
339 private void onRequestHeadersSent() {
340 Runnable task = new Runnable() {
341 public void run() {
342 synchronized (mNativeStreamLock) {
343 if (isDone()) {
344 return;
345 }
346 if (doesMethodAllowWriteData(mInitialMethod)) {
347 mWriteState = STATE_WAITING_ON_WRITE;
348 } else {
349 mWriteState = STATE_WRITING_DONE;
350 }
351 }
352
353 try {
354 mCallback.onRequestHeadersSent(CronetBidirectionalStream.thi s);
355 } catch (Exception e) {
356 onCallbackException(e);
357 }
358 }
359 };
360 postTaskToExecutor(task);
361 }
362
363 /**
364 * Called when the final set of headers, after all redirects,
365 * is received. Can only be called once for each stream.
366 */
367 @SuppressWarnings("unused")
368 @CalledByNative
369 private void onResponseHeadersReceived(int httpStatusCode, String[] headers) {
370 mResponseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode, heade rs);
371 Runnable task = new Runnable() {
372 public void run() {
373 synchronized (mNativeStreamLock) {
374 if (isDone()) {
375 return;
376 }
377 mStreamState = STATE_WAITING_ON_READ;
378 }
379
380 try {
381 mCallback.onResponseHeadersReceived(
382 CronetBidirectionalStream.this, mResponseInfo);
383 } catch (Exception e) {
384 onCallbackException(e);
385 }
386 }
387 };
388 postTaskToExecutor(task);
389 }
390
391 @SuppressWarnings("unused")
392 @CalledByNative
393 private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
394 long receivedBytesCount) {
395 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
396 if (byteBuffer.position() != initialPosition) {
397 failWithException(
398 new CronetException("ByteBuffer modified externally during r ead", null));
399 return;
400 }
401 if (bytesRead < 0 || initialPosition + bytesRead > byteBuffer.limit()) {
402 failWithException(new CronetException("Invalid number of bytes read" , null));
403 return;
404 }
405 if (mOnReadCompletedTask == null) {
406 mOnReadCompletedTask = new OnReadCompletedRunnable();
407 }
408 byteBuffer.position(initialPosition + bytesRead);
409 mOnReadCompletedTask.mByteBuffer = byteBuffer;
410 mOnReadCompletedTask.mEndOfStream = (bytesRead == 0);
411 postTaskToExecutor(mOnReadCompletedTask);
412 }
413
414 @SuppressWarnings("unused")
415 @CalledByNative
416 private void onWriteCompleted(final ByteBuffer byteBuffer, int initialPositi on) {
417 if (byteBuffer.position() != initialPosition) {
418 failWithException(
419 new CronetException("ByteBuffer modified externally during w rite", null));
420 return;
421 }
422 if (mOnWriteCompletedTask == null) {
423 mOnWriteCompletedTask = new OnWriteCompletedRunnable();
424 }
425 // Current implementation always writes the complete buffer.
426 byteBuffer.position(byteBuffer.limit());
427 mOnWriteCompletedTask.mByteBuffer = byteBuffer;
428 postTaskToExecutor(mOnWriteCompletedTask);
429 }
430
431 @SuppressWarnings("unused")
432 @CalledByNative
433 private void onResponseTrailersReceived(String[] trailers) {
434 final UrlResponseInfo.HeaderBlock trailersBlock =
435 new UrlResponseInfo.HeaderBlock(headersListFromStrings(trailers) );
436 Runnable task = new Runnable() {
437 public void run() {
438 synchronized (mNativeStreamLock) {
439 if (isDone()) {
440 return;
441 }
442 }
443 try {
444 mCallback.onResponseTrailersReceived(
445 CronetBidirectionalStream.this, mResponseInfo, trail ersBlock);
446 } catch (Exception e) {
447 onCallbackException(e);
448 }
449 }
450 };
451 postTaskToExecutor(task);
452 }
453
454 @SuppressWarnings("unused")
455 @CalledByNative
456 private void onError(final int nativeError, final String errorString, long r eceivedBytesCount) {
457 if (mResponseInfo != null) {
458 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
459 }
460 failWithException(new CronetException(
461 "Exception in BidirectionalStream: " + errorString, nativeError) );
462 }
463
464 /**
465 * Called when request is canceled, no callbacks will be called afterwards.
466 */
467 @SuppressWarnings("unused")
468 @CalledByNative
469 private void onCanceled() {
470 Runnable task = new Runnable() {
471 public void run() {
472 try {
473 mCallback.onCanceled(CronetBidirectionalStream.this, mRespon seInfo);
474 } catch (Exception e) {
475 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onCance led method", e);
476 }
477 }
478 };
479 postTaskToExecutor(task);
480 }
481
482 @VisibleForTesting
483 public void setOnDestroyedCallbackForTests(Runnable onDestroyedCallbackForTe sts) {
xunjieli 2015/12/18 19:11:11 nit: ForTests vs ForTesting. Should we standardize
mef 2015/12/29 20:36:54 Yes, and ForTesting is more consistent with @Visib
484 mOnDestroyedCallbackForTests = onDestroyedCallbackForTests;
485 }
486
487 /**
488 * Posts task to application Executor. Used for callbacks
489 * and other tasks that should not be executed on network thread.
490 */
491 private void postTaskToExecutor(Runnable task) {
492 try {
493 mExecutor.execute(task);
494 } catch (RejectedExecutionException failException) {
495 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex ecutor",
496 failException);
497 // If posting a task throws an exception, then there is no choice
498 // but to cancel the stream.
499 cancel();
500 }
501 }
502
503 private ArrayList<Map.Entry<String, String>> headersListFromStrings(String[] headers) {
xunjieli 2015/12/18 19:11:12 nit: private static.
mef 2015/12/29 20:36:54 Done.
504 ArrayList<Map.Entry<String, String>> headersList =
505 new ArrayList<Map.Entry<String, String>>();
506 for (int i = 0; i < headers.length; i += 2) {
507 headersList.add(new AbstractMap.SimpleImmutableEntry<String, String> (
508 headers[i], headers[i + 1]));
509 }
510 return headersList;
511 }
512
513 private String[] stringsFromHeaderList(ArrayList<Map.Entry<String, String>> headersList) {
xunjieli 2015/12/18 19:11:12 nit: private static.
mef 2015/12/29 20:36:54 Done.
514 String headersArray[] = new String[headersList.size() * 2];
515 int i = 0;
516 for (Map.Entry<String, String> requestHeader : headersList) {
517 headersArray[i++] = requestHeader.getKey();
518 headersArray[i++] = requestHeader.getValue();
519 }
520 return headersArray;
521 }
522
523 private UrlResponseInfo prepareResponseInfoOnNetworkThread(
524 int httpStatusCode, String[] headers) {
525 long nativeStream;
526 synchronized (mNativeStreamLock) {
527 if (mNativeStream == 0) {
528 return null;
529 }
530 // This method is running on network thread, so even if
531 // mUrlRequestAdapter is set to 0 from another thread the actual
532 // deletion of the adapter is posted to network thread, so it is
533 // safe to preserve and use urlRequestAdapter outside the lock.
xunjieli 2015/12/18 19:11:11 nit: need to update comment. s/mUrlRequestAdapter/
mef 2015/12/29 20:36:54 Done.
534 nativeStream = mNativeStream;
535 }
536
537 ArrayList<String> urlChain = new ArrayList<String>();
538 urlChain.add(mInitialUrl);
539
540 boolean wasCached = false;
541 String httpStatusText = "";
542 String negotiatedProtocol = nativeGetNegotiatedProtocol(nativeStream);
543 String proxyServer = null;
544
545 UrlResponseInfo responseInfo = new UrlResponseInfo(urlChain, httpStatusC ode, httpStatusText,
546 headersListFromStrings(headers), wasCached, negotiatedProtocol, proxyServer);
547 return responseInfo;
548 }
549
550 private void destroyNativeStream(boolean sendOnCanceled) {
551 synchronized (mNativeStreamLock) {
552 Log.i(CronetUrlRequestContext.LOG_TAG, "destroyNativeStream " + this .toString());
553 if (mNativeStream == 0) {
554 return;
555 }
556 nativeDestroy(mNativeStream, sendOnCanceled);
557 mRequestContext.onRequestDestroyed();
558 mNativeStream = 0;
559 if (mOnDestroyedCallbackForTests != null) {
560 mOnDestroyedCallbackForTests.run();
561 }
562 }
563 }
564
565 /**
566 * If callback method throws an exception, stream gets canceled
567 * and exception is reported via onFailed callback.
568 * Only called on the Executor.
569 */
570 private void onCallbackException(Exception e) {
571 CronetException streamError =
572 new CronetException("CalledByNative method has thrown an excepti on", e);
573 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative meth od", e);
574 // Do not call into listener if request is complete.
575 synchronized (mNativeStreamLock) {
576 if (isDone()) {
577 return;
578 }
579 destroyNativeStream(false);
580 }
581 try {
582 mCallback.onFailed(this, mResponseInfo, streamError);
583 } catch (Exception failException) {
584 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of faile d request",
585 failException);
586 }
587 }
588
589 /**
590 * Fails the stream with an exception. Can be called on any thread.
591 */
592 private void failWithException(final CronetException exception) {
593 Runnable task = new Runnable() {
594 public void run() {
595 synchronized (mNativeStreamLock) {
596 if (isDone()) {
597 return;
598 }
599 mStreamState = STATE_ERROR;
600 destroyNativeStream(false);
601 }
602 try {
603 mCallback.onFailed(CronetBidirectionalStream.this, mResponse Info, exception);
604 } catch (Exception e) {
605 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onError method", e);
606 }
607 }
608 };
609 postTaskToExecutor(task);
610 }
611
612 // Native methods are implemented in cronet_bidirectional_stream.cc.
613 private native long nativeCreateBidirectionalStream(long urlRequestContextAd apter);
614
615 @NativeClassQualifiedName("CronetBidirectionalStream")
616 private native int nativeStart(
617 long nativePtr, String url, String method, String[] headers, boolean endOfStream);
618
619 @NativeClassQualifiedName("CronetBidirectionalStream")
620 private native boolean nativeReadData(
621 long nativePtr, ByteBuffer byteBuffer, int position, int capacity);
622
623 @NativeClassQualifiedName("CronetBidirectionalStream")
624 private native boolean nativeWriteData(
625 long nativePtr, ByteBuffer byteBuffer, int position, int capacity, b oolean endOfStream);
626
627 @NativeClassQualifiedName("CronetBidirectionalStream")
628 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled);
629
630 @NativeClassQualifiedName("CronetBidirectionalStream")
631 private native String nativeGetNegotiatedProtocol(long nativePtr);
632 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698