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

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 4 years, 11 months 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 org.chromium.base.Log;
8 import org.chromium.base.VisibleForTesting;
9 import org.chromium.base.annotations.CalledByNative;
10 import org.chromium.base.annotations.JNINamespace;
11 import org.chromium.base.annotations.NativeClassQualifiedName;
12
13 import java.nio.ByteBuffer;
14 import java.util.AbstractMap;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.concurrent.Executor;
19 import java.util.concurrent.RejectedExecutionException;
20
21 import javax.annotation.concurrent.GuardedBy;
22
23 /**
24 * BidirectionalStream implementation using Chromium network stack.
25 * All @CallByNative methods are called on native network thread
26 * and post tasks with callback calls onto Executor. Upon return from callback n ative
27 * stream is called on executor thread and posts native tasks to native network thread.
28 */
29 @JNINamespace("cronet")
30 class CronetBidirectionalStream extends BidirectionalStream {
31 /**
32 * States of BidirectionalStream are tracked in mReadState and mWriteState.
33 * The write state is separated out as it changes independently of the strea m state.
34 * There is one initial state - State.NOT_STARTED. There is one final state - State.SUCCESS,
35 * reached after State.READING_DONE and State.WRITING_DONE. There are 2 exce ption states -
36 * State.CANCELED and State.ERROR, which can be reached from any other state except
37 * State.SUCCESS.
38 */
39 private enum State {
40 /* Initial state, stream not started. */
41 NOT_STARTED,
42 /* Stream started, request headers are sent. */
43 STARTED,
44 /* Waiting for {@code read()} to be called. */
45 WAITING_ON_READ,
46 /* Reading from the remote, {@code onReadCompleted()} callback will be c alled when done. */
47 READING,
48 /* There is no more data to read and stream is half-closed by the remote side. */
49 READING_DONE,
50 /* Stream is canceled. */
51 CANCELED,
52 /* Error has occured, stream is closed. */
53 ERROR,
54 /* Reading and writing is done, and the stream is closed successfully. * /
55 SUCCESS,
56 /* Waiting for {@code write()} to be called. */
57 WAITING_ON_WRITE,
58 /* Writing to the remote, {@code onWriteCompleted()} callback will be ca lled when done. */
59 WRITING,
60 /* Writing the last frame, so {@code State.WRITING_DONE} will be set upo n completion. */
61 WRITING_END_OF_STREAM,
62 /* There is no more data to write and stream is half-closed by the local side. */
63 WRITING_DONE,
64 }
65
66 private final CronetUrlRequestContext mRequestContext;
67 private final Executor mExecutor;
68 private final Callback mCallback;
69 private final String mInitialUrl;
70 private final int mInitialPriority;
71 private final String mInitialMethod;
72 private final ArrayList<Map.Entry<String, String>> mRequestHeaders;
73
74 /*
75 * Synchronize access to mNativeStream, mReadState and mWriteState.
76 */
77 private final Object mNativeStreamLock = new Object();
78
79 /* Native BidirectionalStream object, owned by CronetBidirectionalStream. */
80 @GuardedBy("mNativeStreamLock") private long mNativeStream;
81
82 /**
83 * Read state is tracking reading flow.
84 * / <--- READING <--- \
85 * | |
86 * \ /
87 * NOT_STARTED -> STARTED --> WAITING_ON_READ -> READING_DONE -> SUCCESS
88 */
89 @GuardedBy("mNativeStreamLock") private State mReadState = State.NOT_STARTED ;
90
91 /**
92 * Write state is tracking writing flow.
93 * / <--- WRITING <--- \
94 * | |
95 * \ /
96 * NOT_STARTED -> STARTED --> WAITING_ON_WRITE -> WRITING_END_OF_STREAM -> W RITING_DONE ->
97 * SUCCESS
98 */
99 @GuardedBy("mNativeStreamLock") private State mWriteState = State.NOT_STARTE D;
100
101 private UrlResponseInfo mResponseInfo;
102
103 /*
104 * OnReadCompleted callback is repeatedly invoked when each read is complete d, so it
105 * is cached as a member variable.
106 */
107 private OnReadCompletedRunnable mOnReadCompletedTask;
108
109 /*
110 * OnWriteCompleted callback is repeatedly invoked when each write is comple ted, so it
111 * is cached as a member variable.
112 */
113 private OnWriteCompletedRunnable mOnWriteCompletedTask;
114
115 private Runnable mOnDestroyedCallbackForTesting;
116
117 private final class OnReadCompletedRunnable implements Runnable {
118 // Buffer passed back from current invocation of onReadCompleted.
119 ByteBuffer mByteBuffer;
120 // End of stream flag from current invocation of onReadCompleted.
121 boolean mEndOfStream;
122
123 @Override
124 public void run() {
125 try {
126 synchronized (mNativeStreamLock) {
127 if (isDoneLocked() || mNativeStream == 0) {
pauljensen 2016/01/11 20:05:45 how could mNativeStream == 0 here but isDoneLocked
mef 2016/01/11 23:22:50 Good point. Done.
128 return;
129 }
130 if (mEndOfStream) {
131 mReadState = State.READING_DONE;
132 if (maybeSucceeded()) return;
pauljensen 2016/01/11 20:05:45 our style isn't to put return on the same line; we
mef 2016/01/11 23:22:50 Done.
133 } else {
134 mReadState = State.WAITING_ON_READ;
135 }
136 }
137 // Null out mByteBuffer, to pass buffer ownership to callback.
138 ByteBuffer buffer = mByteBuffer;
pauljensen 2016/01/11 20:05:45 I think there is a race here because after line 13
mef 2016/01/11 23:22:50 Done.
139 mByteBuffer = null;
pauljensen 2016/01/11 20:05:45 this line should be moved up above all the "return
mef 2016/01/11 23:22:50 Done.
pauljensen 2016/01/12 16:55:41 Can we make this change to CronetUrlRequest.java t
140 mCallback.onReadCompleted(CronetBidirectionalStream.this, mRespo nseInfo, buffer);
141 } catch (Exception e) {
142 onCallbackException(e);
143 }
144 }
145 }
146
147 private final class OnWriteCompletedRunnable implements Runnable {
148 // Buffer passed back from current invocation of onWriteCompleted.
149 ByteBuffer mByteBuffer;
150
151 @Override
152 public void run() {
153 try {
154 synchronized (mNativeStreamLock) {
155 if (isDoneLocked() || mNativeStream == 0) {
156 return;
157 }
158 if (mWriteState == State.WRITING_END_OF_STREAM) {
159 mWriteState = State.WRITING_DONE;
pauljensen 2016/01/11 20:05:45 did we necessarily write all of the buffer's data
mef 2016/01/11 23:22:50 Current implementation always writes the complete
160 if (maybeSucceeded()) return;
pauljensen 2016/01/11 20:05:45 our style isn't to put return on the same line; we
mef 2016/01/11 23:22:50 Done.
161 } else {
162 mWriteState = State.WAITING_ON_WRITE;
163 }
164 }
165 // Null out mByteBuffer, to pass buffer ownership to callback.
166 ByteBuffer buffer = mByteBuffer;
167 mByteBuffer = null;
pauljensen 2016/01/11 20:05:45 this line should be moved up above all the "return
mef 2016/01/11 23:22:50 Done.
168 mCallback.onWriteCompleted(CronetBidirectionalStream.this, mResp onseInfo, buffer);
169 } catch (Exception e) {
170 onCallbackException(e);
171 }
172 }
173 }
174
175 CronetBidirectionalStream(CronetUrlRequestContext requestContext, long urlRe questContextAdapter,
176 String url, @BidirectionalStream.Builder.StreamPriority int priority , Callback callback,
177 Executor executor, String httpMethod, List<Map.Entry<String, String> > requestHeaders) {
178 mRequestContext = requestContext;
179 mInitialUrl = url;
180 mInitialPriority = convertStreamPriority(priority);
181 mCallback = callback;
182 mExecutor = executor;
183 mInitialMethod = httpMethod;
184 mRequestHeaders = new ArrayList<Map.Entry<String, String>>(requestHeader s);
185 }
186
187 @GuardedBy("mNativeStreamLock")
188 private boolean maybeSucceeded() {
189 if (mReadState != State.READING_DONE || mWriteState != State.WRITING_DON E) {
190 return false;
191 }
192
193 mReadState = mWriteState = State.SUCCESS;
194 Runnable task = new Runnable() {
195 public void run() {
196 synchronized (mNativeStreamLock) {
197 if (isDoneLocked()) {
198 return;
199 }
200 // Destroy native stream first, so request context could be shut
201 // down from the listener.
202 destroyNativeStream(false);
203 }
204 try {
205 mCallback.onSucceeded(CronetBidirectionalStream.this, mRespo nseInfo);
206 } catch (Exception e) {
207 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onSucce eded method", e);
208 }
209 }
210 };
211 postTaskToExecutor(task);
212 return true;
213 }
214
215 private static boolean doesMethodAllowWriteData(String methodName) {
216 return !methodName.equals("GET") && !methodName.equals("HEAD");
217 }
218
219 @Override
220 public void start() {
221 synchronized (mNativeStreamLock) {
222 if (mReadState != State.NOT_STARTED) {
223 throw new IllegalStateException("Stream is already started.");
224 }
225 try {
226 mNativeStream = nativeCreateBidirectionalStream(
227 mRequestContext.getUrlRequestContextAdapter());
228 mRequestContext.onRequestStarted();
229 String headers[] = stringsFromHeaderList(mRequestHeaders);
230 // Non-zero startResult means an argument error.
231 int startResult = nativeStart(mNativeStream, mInitialUrl, mIniti alPriority,
232 mInitialMethod, headers, !doesMethodAllowWriteData(mInit ialMethod));
233 if (startResult == -1) {
234 throw new IllegalArgumentException("Invalid http method " + mInitialMethod);
235 }
236 if (startResult > 0) {
237 int headerPos = startResult - 1;
238 throw new IllegalArgumentException(
239 "Invalid header " + headers[headerPos] + "=" + heade rs[headerPos + 1]);
240 }
241 mReadState = mWriteState = State.STARTED;
242 } catch (RuntimeException e) {
243 // If there's an exception, cleanup and then throw the
244 // exception to the caller.
245 destroyNativeStream(false);
246 throw e;
247 }
248 }
249 }
250
251 @Override
252 public void read(ByteBuffer buffer) {
253 synchronized (mNativeStreamLock) {
254 if (!buffer.hasRemaining()) {
255 throw new IllegalArgumentException("ByteBuffer is already full." );
256 }
257 if (mReadState != State.WAITING_ON_READ) {
258 throw new IllegalStateException("Unexpected read attempt.");
259 }
260 if (isDoneLocked()) {
261 return;
262 }
263 mReadState = State.READING;
264 if (!nativeReadData(mNativeStream, buffer, buffer.position(), buffer .limit())) {
265 // Still waiting on read. This is just to have consistent
266 // behavior with the other error cases.
267 mReadState = State.WAITING_ON_READ;
268 // Since accessing byteBuffer's memory failed, it's presumably
269 // not a direct ByteBuffer.
270 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
271 }
272 }
273 }
274
275 @Override
276 public void write(ByteBuffer buffer, boolean endOfStream) {
277 synchronized (mNativeStreamLock) {
278 if (!buffer.hasRemaining() && !endOfStream) {
279 throw new IllegalArgumentException("Empty buffer before end of s tream.");
280 }
281 if (mWriteState != State.WAITING_ON_WRITE) {
282 throw new IllegalStateException("Unexpected write attempt.");
283 }
284 if (isDoneLocked()) {
285 return;
286 }
287 mWriteState = endOfStream ? State.WRITING_END_OF_STREAM : State.WRIT ING;
288 if (!nativeWriteData(
289 mNativeStream, buffer, buffer.position(), buffer.limit() , endOfStream)) {
290 // Still waiting on write. This is just to have consistent
291 // behavior with the other error cases.
292 mWriteState = State.WAITING_ON_WRITE;
293 // Since accessing byteBuffer's memory failed, it's presumably
294 // not a direct ByteBuffer.
295 throw new IllegalArgumentException("byteBuffer must be a direct ByteBuffer.");
296 }
297 }
298 }
299
300 @Override
301 public void ping(PingCallback callback, Executor executor) {
302 // TODO(mef): May be last thing to be implemented on Android.
303 throw new UnsupportedOperationException("ping is not supported yet.");
304 }
305
306 @Override
307 public void windowUpdate(int windowSizeIncrement) {
308 // TODO(mef): Understand the needs and semantics of this method.
309 throw new UnsupportedOperationException("windowUpdate is not supported y et.");
310 }
311
312 @Override
313 public void cancel() {
314 synchronized (mNativeStreamLock) {
315 if (isDoneLocked() || mReadState == State.NOT_STARTED) {
316 return;
317 }
318 mReadState = mWriteState = State.CANCELED;
319 destroyNativeStream(true);
320 }
321 }
322
323 @Override
324 public boolean isDone() {
325 synchronized (mNativeStreamLock) {
326 return isDoneLocked();
327 }
328 }
329
330 @GuardedBy("mNativeStreamLock")
331 private boolean isDoneLocked() {
332 return mReadState != State.NOT_STARTED && mNativeStream == 0;
333 }
334
335 @SuppressWarnings("unused")
336 @CalledByNative
337 private void onRequestHeadersSent() {
338 Runnable task = new Runnable() {
339 public void run() {
340 synchronized (mNativeStreamLock) {
341 if (isDoneLocked()) {
342 return;
343 }
344 if (doesMethodAllowWriteData(mInitialMethod)) {
345 mWriteState = State.WAITING_ON_WRITE;
346 } else {
347 mWriteState = State.WRITING_DONE;
348 }
349 }
350
351 try {
352 mCallback.onRequestHeadersSent(CronetBidirectionalStream.thi s);
353 } catch (Exception e) {
354 onCallbackException(e);
355 }
356 }
357 };
358 postTaskToExecutor(task);
359 }
360
361 /**
362 * Called when the final set of headers, after all redirects,
363 * is received. Can only be called once for each stream.
364 */
365 @SuppressWarnings("unused")
366 @CalledByNative
367 private void onResponseHeadersReceived(int httpStatusCode, String[] headers) {
368 mResponseInfo = prepareResponseInfoOnNetworkThread(httpStatusCode, heade rs);
369 Runnable task = new Runnable() {
370 public void run() {
371 synchronized (mNativeStreamLock) {
372 if (isDoneLocked()) {
373 return;
374 }
375 mReadState = State.WAITING_ON_READ;
376 }
377
378 try {
379 mCallback.onResponseHeadersReceived(
380 CronetBidirectionalStream.this, mResponseInfo);
381 } catch (Exception e) {
382 onCallbackException(e);
383 }
384 }
385 };
386 postTaskToExecutor(task);
387 }
388
389 @SuppressWarnings("unused")
390 @CalledByNative
391 private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
392 int initialLimit, long receivedBytesCount) {
393 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
394 if (byteBuffer.position() != initialPosition || byteBuffer.limit() != in itialLimit) {
395 failWithException(
396 new CronetException("ByteBuffer modified externally during r ead", null));
397 return;
398 }
399 if (bytesRead < 0 || initialPosition + bytesRead > byteBuffer.limit()) {
400 failWithException(new CronetException("Invalid number of bytes read" , null));
401 return;
402 }
403 if (mOnReadCompletedTask == null) {
404 mOnReadCompletedTask = new OnReadCompletedRunnable();
405 }
406 byteBuffer.position(initialPosition + bytesRead);
407 mOnReadCompletedTask.mByteBuffer = byteBuffer;
408 mOnReadCompletedTask.mEndOfStream = (bytesRead == 0);
409 postTaskToExecutor(mOnReadCompletedTask);
410 }
411
412 @SuppressWarnings("unused")
413 @CalledByNative
414 private void onWriteCompleted(
415 final ByteBuffer byteBuffer, int initialPosition, int initialLimit) {
416 if (byteBuffer.position() != initialPosition || byteBuffer.limit() != in itialLimit) {
417 failWithException(
418 new CronetException("ByteBuffer modified externally during w rite", null));
419 return;
420 }
421 if (mOnWriteCompletedTask == null) {
422 mOnWriteCompletedTask = new OnWriteCompletedRunnable();
423 }
424 // Current implementation always writes the complete buffer.
425 byteBuffer.position(byteBuffer.limit());
426 mOnWriteCompletedTask.mByteBuffer = byteBuffer;
427 postTaskToExecutor(mOnWriteCompletedTask);
428 }
429
430 @SuppressWarnings("unused")
431 @CalledByNative
432 private void onResponseTrailersReceived(String[] trailers) {
433 final UrlResponseInfo.HeaderBlock trailersBlock =
434 new UrlResponseInfo.HeaderBlock(headersListFromStrings(trailers) );
435 Runnable task = new Runnable() {
436 public void run() {
437 synchronized (mNativeStreamLock) {
438 if (isDoneLocked()) {
439 return;
440 }
441 }
442 try {
443 mCallback.onResponseTrailersReceived(
444 CronetBidirectionalStream.this, mResponseInfo, trail ersBlock);
445 } catch (Exception e) {
446 onCallbackException(e);
447 }
448 }
449 };
450 postTaskToExecutor(task);
451 }
452
453 @SuppressWarnings("unused")
454 @CalledByNative
455 private void onError(final int nativeError, final String errorString, long r eceivedBytesCount) {
456 if (mResponseInfo != null) {
457 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
458 }
459 failWithException(new CronetException(
460 "Exception in BidirectionalStream: " + errorString, nativeError) );
461 }
462
463 /**
464 * Called when request is canceled, no callbacks will be called afterwards.
465 */
466 @SuppressWarnings("unused")
467 @CalledByNative
468 private void onCanceled() {
469 Runnable task = new Runnable() {
470 public void run() {
471 try {
472 mCallback.onCanceled(CronetBidirectionalStream.this, mRespon seInfo);
473 } catch (Exception e) {
474 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onCance led method", e);
475 }
476 }
477 };
478 postTaskToExecutor(task);
479 }
480
481 @VisibleForTesting
482 public void setOnDestroyedCallbackForTesting(Runnable onDestroyedCallbackFor Testing) {
483 mOnDestroyedCallbackForTesting = onDestroyedCallbackForTesting;
484 }
485
486 /**
487 * Posts task to application Executor. Used for callbacks
488 * and other tasks that should not be executed on network thread.
489 */
490 private void postTaskToExecutor(Runnable task) {
491 try {
492 mExecutor.execute(task);
493 } catch (RejectedExecutionException failException) {
494 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex ecutor",
495 failException);
496 // If posting a task throws an exception, then there is no choice
497 // but to cancel the stream.
498 cancel();
499 }
500 }
501
502 private static ArrayList<Map.Entry<String, String>> headersListFromStrings(S tring[] headers) {
503 ArrayList<Map.Entry<String, String>> headersList =
504 new ArrayList<Map.Entry<String, String>>(headers.length / 2);
505 for (int i = 0; i < headers.length; i += 2) {
506 headersList.add(new AbstractMap.SimpleImmutableEntry<String, String> (
507 headers[i], headers[i + 1]));
508 }
509 return headersList;
510 }
511
512 private static String[] stringsFromHeaderList(
513 ArrayList<Map.Entry<String, String>> headersList) {
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 // mNativeStream 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 nativeStream outside the lock.
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 static int convertStreamPriority(
551 @BidirectionalStream.Builder.StreamPriority int priority) {
552 switch (priority) {
553 case Builder.STREAM_PRIORITY_IDLE:
554 return RequestPriority.IDLE;
555 case Builder.STREAM_PRIORITY_LOWEST:
556 return RequestPriority.LOWEST;
557 case Builder.STREAM_PRIORITY_LOW:
558 return RequestPriority.LOW;
559 case Builder.STREAM_PRIORITY_MEDIUM:
560 return RequestPriority.MEDIUM;
561 case Builder.STREAM_PRIORITY_HIGHEST:
562 return RequestPriority.HIGHEST;
563 default:
564 return RequestPriority.MEDIUM;
565 }
566 }
567
568 private void destroyNativeStream(boolean sendOnCanceled) {
569 synchronized (mNativeStreamLock) {
570 Log.i(CronetUrlRequestContext.LOG_TAG, "destroyNativeStream " + this .toString());
571 if (mNativeStream == 0) {
572 return;
573 }
574 nativeDestroy(mNativeStream, sendOnCanceled);
575 mNativeStream = 0;
576 mRequestContext.onRequestDestroyed();
577 if (mOnDestroyedCallbackForTesting != null) {
578 mOnDestroyedCallbackForTesting.run();
579 }
580 }
581 }
582
583 /**
584 * If callback method throws an exception, stream gets canceled
585 * and exception is reported via onFailed callback.
586 * Only called on the Executor.
587 */
588 private void onCallbackException(Exception e) {
589 CronetException streamError =
590 new CronetException("CalledByNative method has thrown an excepti on", e);
591 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative meth od", e);
592 // Do not call into listener if request is complete.
593 synchronized (mNativeStreamLock) {
594 if (isDoneLocked()) {
595 return;
596 }
pauljensen 2016/01/11 20:05:45 should we insert "mReadState = mWriteState = State
mef 2016/01/11 23:22:50 Done.
597 destroyNativeStream(false);
598 }
599 try {
600 mCallback.onFailed(this, mResponseInfo, streamError);
601 } catch (Exception failException) {
602 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of faile d request",
603 failException);
604 }
605 }
606
607 /**
608 * Fails the stream with an exception. Can be called on any thread.
609 */
610 private void failWithException(final CronetException exception) {
611 Runnable task = new Runnable() {
612 public void run() {
613 synchronized (mNativeStreamLock) {
614 if (isDoneLocked()) {
615 return;
616 }
617 mReadState = mWriteState = State.ERROR;
618 destroyNativeStream(false);
619 }
620 try {
621 mCallback.onFailed(CronetBidirectionalStream.this, mResponse Info, exception);
622 } catch (Exception e) {
623 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onError method", e);
624 }
625 }
626 };
627 postTaskToExecutor(task);
628 }
629
630 // Native methods are implemented in cronet_bidirectional_stream_adapter.cc.
631 private native long nativeCreateBidirectionalStream(long urlRequestContextAd apter);
632
633 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
634 private native int nativeStart(long nativePtr, String url, int priority, Str ing method,
635 String[] headers, boolean endOfStream);
636
637 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
638 private native boolean nativeReadData(
639 long nativePtr, ByteBuffer byteBuffer, int position, int limit);
640
641 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
642 private native boolean nativeWriteData(
643 long nativePtr, ByteBuffer byteBuffer, int position, int limit, bool ean endOfStream);
644
645 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
646 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled);
647
648 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
649 private native String nativeGetNegotiatedProtocol(long nativePtr);
650 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698