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

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: Self review. 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 * {@link BidirectionalStream} implementation using Chromium network stack.
25 * All @CalledByNative methods are called on the native network thread
26 * and post tasks with callback calls onto Executor. Upon return from callback, the native
27 * stream is called on Executor thread and posts native tasks to the native netw ork 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 read state.
34 * There is one initial state - State.NOT_STARTED. There is one normal final state -
xunjieli 2016/01/21 19:13:08 nit: single dash reads very much like a minus sign
mef 2016/01/22 14:33:44 Interesting. Double dash seems unusual to me. :) I
xunjieli 2016/01/22 14:52:23 Acknowledged.
35 * State.SUCCESS, reached after State.READING_DONE and State.WRITING_DONE. T here are two
36 * exceptional final states - State.CANCELED and State.ERROR, which can be r eached from
37 * any other non-final state.
38 */
39 private enum State {
40 /* Initial state, stream not started. */
41 NOT_STARTED,
42 /* Stream started, request headers are being sent. */
43 STARTED,
44 /* Waiting for {@code read()} to be called. */
45 WAITING_ON_READ,
xunjieli 2016/01/21 19:13:08 Should this say WAITING_FOR_READ instead of ON? Si
mef 2016/01/22 14:33:44 Excellent suggestion! Done.
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. * /
xunjieli 2016/01/21 19:13:08 nit: s/is/are. Reading and writing are done
mef 2016/01/22 14:33:44 Done.
55 SUCCESS,
56 /* Waiting for {@code write()} to be called. */
57 WAITING_ON_WRITE,
xunjieli 2016/01/21 19:13:08 Should this say WAITING_FOR_WRITE instead of ON? S
mef 2016/01/22 14:33:44 Done.
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 String mRequestHeaders[];
73
74 /*
75 * Synchronize access to mNativeStream, mReadState and mWriteState.
xunjieli 2016/01/21 19:13:08 nit: s/Synchronize/Synchronizes
mef 2016/01/22 14:33:44 Done.
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
xunjieli 2016/01/21 19:13:08 Great flow diagram! This makes things much easier
mef 2016/01/22 14:33:44 Kudos to Paul for suggestion! I don't think it get
xunjieli 2016/01/22 14:52:23 Acknowledged. Yep you are right. Didn't realize th
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 // Null out mByteBuffer, to pass buffer ownership to callback or release if done.
127 ByteBuffer buffer = mByteBuffer;
128 mByteBuffer = null;
129 synchronized (mNativeStreamLock) {
130 if (isDoneLocked()) {
131 return;
132 }
133 if (mEndOfStream) {
134 mReadState = State.READING_DONE;
135 if (maybeSucceededLocked()) {
136 return;
137 }
138 } else {
139 mReadState = State.WAITING_ON_READ;
140 }
141 }
142 mCallback.onReadCompleted(CronetBidirectionalStream.this, mRespo nseInfo, buffer);
143 } catch (Exception e) {
144 onCallbackException(e);
145 }
146 }
147 }
148
149 private final class OnWriteCompletedRunnable implements Runnable {
150 // Buffer passed back from current invocation of onWriteCompleted.
151 ByteBuffer mByteBuffer;
152
153 @Override
154 public void run() {
155 try {
156 // Null out mByteBuffer, to pass buffer ownership to callback or release if done.
157 ByteBuffer buffer = mByteBuffer;
158 mByteBuffer = null;
159 synchronized (mNativeStreamLock) {
160 if (isDoneLocked()) {
161 return;
162 }
163 if (mWriteState == State.WRITING_END_OF_STREAM) {
164 mWriteState = State.WRITING_DONE;
165 if (maybeSucceededLocked()) {
166 return;
167 }
168 } else {
169 mWriteState = State.WAITING_ON_WRITE;
170 }
171 }
172 mCallback.onWriteCompleted(CronetBidirectionalStream.this, mResp onseInfo, buffer);
173 } catch (Exception e) {
174 onCallbackException(e);
175 }
176 }
177 }
178
179 CronetBidirectionalStream(CronetUrlRequestContext requestContext, String url ,
180 @BidirectionalStream.Builder.StreamPriority int priority, Callback c allback,
181 Executor executor, String httpMethod, List<Map.Entry<String, String> > requestHeaders) {
182 mRequestContext = requestContext;
183 mInitialUrl = url;
184 mInitialPriority = convertStreamPriority(priority);
185 mCallback = callback;
186 mExecutor = executor;
187 mInitialMethod = httpMethod;
188 mRequestHeaders = stringsFromHeaderList(requestHeaders);
189 }
190
191 @GuardedBy("mNativeStreamLock")
192 private boolean maybeSucceededLocked() {
xunjieli 2016/01/21 19:13:08 - Suggest making this method return void. (Early r
mef 2016/01/22 14:33:44 I'm not sure I understand, the result is important
xunjieli 2016/01/22 14:52:23 I think I missed a part of the logic in the first
mef 2016/01/22 17:36:06 Done.
193 if (mReadState != State.READING_DONE || mWriteState != State.WRITING_DON E) {
194 return false;
195 }
196
197 mReadState = mWriteState = State.SUCCESS;
198 postTaskToExecutor(new Runnable() {
199 public void run() {
200 synchronized (mNativeStreamLock) {
201 if (isDoneLocked()) {
202 return;
203 }
204 // Destroy native stream first, so request context could be shut
xunjieli 2016/01/21 19:13:08 nit: maybe spell out the "request context" as UrlR
mef 2016/01/22 14:33:43 Done.
205 // down from the listener.
206 destroyNativeStreamLocked(false);
207 }
208 try {
209 mCallback.onSucceeded(CronetBidirectionalStream.this, mRespo nseInfo);
210 } catch (Exception e) {
211 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onSucce eded method", e);
212 }
213 }
214 });
215 return true;
216 }
217
218 private static boolean doesMethodAllowWriteData(String methodName) {
219 return !methodName.equals("GET") && !methodName.equals("HEAD");
220 }
221
222 @Override
223 public void start() {
224 synchronized (mNativeStreamLock) {
225 if (mReadState != State.NOT_STARTED) {
226 throw new IllegalStateException("Stream is already started.");
227 }
228 try {
229 mNativeStream = nativeCreateBidirectionalStream(
230 mRequestContext.getUrlRequestContextAdapter());
231 mRequestContext.onRequestStarted();
232 // Non-zero startResult means an argument error.
233 int startResult = nativeStart(mNativeStream, mInitialUrl, mIniti alPriority,
234 mInitialMethod, mRequestHeaders, !doesMethodAllowWriteDa ta(mInitialMethod));
235 if (startResult == -1) {
236 throw new IllegalArgumentException("Invalid http method " + mInitialMethod);
237 }
238 if (startResult > 0) {
239 int headerPos = startResult - 1;
240 throw new IllegalArgumentException("Invalid header "
241 + mRequestHeaders[headerPos] + "=" + mRequestHeaders [headerPos + 1]);
242 }
243 mReadState = mWriteState = State.STARTED;
244 } catch (RuntimeException e) {
245 // If there's an exception, cleanup and then throw the
xunjieli 2016/01/21 19:13:08 nit: s/cleanup/clean up. (Cleanup is the noun)
mef 2016/01/22 14:33:44 Done.
246 // exception to the caller.
247 destroyNativeStreamLocked(false);
248 throw e;
249 }
250 }
251 }
252
253 @Override
254 public void read(ByteBuffer buffer) {
255 synchronized (mNativeStreamLock) {
256 Preconditions.checkHasRemaining(buffer);
257 Preconditions.checkDirect(buffer);
258 if (mReadState != State.WAITING_ON_READ) {
259 throw new IllegalStateException("Unexpected read attempt.");
260 }
261 if (isDoneLocked()) {
262 return;
263 }
264 mReadState = State.READING;
265 if (!nativeReadData(mNativeStream, buffer, buffer.position(), buffer .limit())) {
266 // Still waiting on read. This is just to have consistent
267 // behavior with the other error cases.
268 mReadState = State.WAITING_ON_READ;
269 throw new IllegalArgumentException("Unable to call native read") ;
270 }
271 }
272 }
273
274 @Override
275 public void write(ByteBuffer buffer, boolean endOfStream) {
276 synchronized (mNativeStreamLock) {
277 Preconditions.checkDirect(buffer);
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 throw new IllegalArgumentException("Unable to call native write" );
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.
301 throw new UnsupportedOperationException("ping is not supported yet.");
302 }
303
304 @Override
305 public void windowUpdate(int windowSizeIncrement) {
306 // TODO(mef): Understand the needs and semantics of this method.
307 throw new UnsupportedOperationException("windowUpdate is not supported y et.");
308 }
309
310 @Override
311 public void cancel() {
312 synchronized (mNativeStreamLock) {
313 if (isDoneLocked() || mReadState == State.NOT_STARTED) {
314 return;
315 }
316 mReadState = mWriteState = State.CANCELED;
317 destroyNativeStreamLocked(true);
318 }
319 }
320
321 @Override
322 public boolean isDone() {
323 synchronized (mNativeStreamLock) {
324 return isDoneLocked();
325 }
326 }
327
328 @GuardedBy("mNativeStreamLock")
329 private boolean isDoneLocked() {
xunjieli 2016/01/21 19:13:08 I believe grabbing the same lock is essentially no
pauljensen 2016/01/22 03:55:31 Locks in Java are re-entrant but I imagine there m
mef 2016/01/22 14:33:44 See previous discussion with Paul. I would imagine
330 return mReadState != State.NOT_STARTED && mNativeStream == 0;
331 }
332
333 @SuppressWarnings("unused")
334 @CalledByNative
335 private void onRequestHeadersSent() {
336 postTaskToExecutor(new Runnable() {
337 public void run() {
338 synchronized (mNativeStreamLock) {
339 if (isDoneLocked()) {
340 return;
341 }
342 if (doesMethodAllowWriteData(mInitialMethod)) {
343 mWriteState = State.WAITING_ON_WRITE;
344 } else {
345 mWriteState = State.WRITING_DONE;
346 }
347 }
348
349 try {
350 mCallback.onRequestHeadersSent(CronetBidirectionalStream.thi s);
351 } catch (Exception e) {
352 onCallbackException(e);
353 }
354 }
355 });
356 }
357
358 /**
359 * Called when the final set of headers, after all redirects,
360 * is received. Can only be called once for each stream.
361 */
362 @SuppressWarnings("unused")
363 @CalledByNative
364 private void onResponseHeadersReceived(int httpStatusCode, String negotiated Protocol,
365 String[] headers, long receivedBytesCount) {
366 mResponseInfo =
367 prepareResponseInfoOnNetworkThread(httpStatusCode, negotiatedPro tocol, headers);
368 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
369 postTaskToExecutor(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 }
387
388 @SuppressWarnings("unused")
389 @CalledByNative
390 private void onReadCompleted(final ByteBuffer byteBuffer, int bytesRead, int initialPosition,
391 int initialLimit, long receivedBytesCount) {
392 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
393 if (byteBuffer.position() != initialPosition || byteBuffer.limit() != in itialLimit) {
394 failWithException(
395 new CronetException("ByteBuffer modified externally during r ead", null));
396 return;
397 }
398 if (bytesRead < 0 || initialPosition + bytesRead > initialLimit) {
399 failWithException(new CronetException("Invalid number of bytes read" , null));
400 return;
401 }
402 if (mOnReadCompletedTask == null) {
403 mOnReadCompletedTask = new OnReadCompletedRunnable();
404 }
405 byteBuffer.position(initialPosition + bytesRead);
406 mOnReadCompletedTask.mByteBuffer = byteBuffer;
407 mOnReadCompletedTask.mEndOfStream = (bytesRead == 0);
408 postTaskToExecutor(mOnReadCompletedTask);
409 }
410
411 @SuppressWarnings("unused")
412 @CalledByNative
413 private void onWriteCompleted(
414 final ByteBuffer byteBuffer, int initialPosition, int initialLimit) {
415 if (byteBuffer.position() != initialPosition || byteBuffer.limit() != in itialLimit) {
416 failWithException(
417 new CronetException("ByteBuffer modified externally during w rite", null));
418 return;
419 }
420 if (mOnWriteCompletedTask == null) {
421 mOnWriteCompletedTask = new OnWriteCompletedRunnable();
422 }
423 // Current implementation always writes the complete buffer.
424 byteBuffer.position(byteBuffer.limit());
425 mOnWriteCompletedTask.mByteBuffer = byteBuffer;
426 postTaskToExecutor(mOnWriteCompletedTask);
427 }
428
429 @SuppressWarnings("unused")
430 @CalledByNative
431 private void onResponseTrailersReceived(String[] trailers) {
432 final UrlResponseInfo.HeaderBlock trailersBlock =
433 new UrlResponseInfo.HeaderBlock(headersListFromStrings(trailers) );
434 postTaskToExecutor(new Runnable() {
435 public void run() {
436 synchronized (mNativeStreamLock) {
437 if (isDoneLocked()) {
438 return;
439 }
440 }
441 try {
442 mCallback.onResponseTrailersReceived(
443 CronetBidirectionalStream.this, mResponseInfo, trail ersBlock);
444 } catch (Exception e) {
445 onCallbackException(e);
446 }
447 }
448 });
449 }
450
451 @SuppressWarnings("unused")
452 @CalledByNative
453 private void onError(final int nativeError, final String errorString, long r eceivedBytesCount) {
454 if (mResponseInfo != null) {
455 mResponseInfo.setReceivedBytesCount(receivedBytesCount);
456 }
457 failWithException(new CronetException(
458 "Exception in BidirectionalStream: " + errorString, nativeError) );
459 }
460
461 /**
462 * Called when request is canceled, no callbacks will be called afterwards.
463 */
464 @SuppressWarnings("unused")
465 @CalledByNative
466 private void onCanceled() {
467 postTaskToExecutor(new Runnable() {
468 public void run() {
469 try {
470 mCallback.onCanceled(CronetBidirectionalStream.this, mRespon seInfo);
471 } catch (Exception e) {
472 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in onCance led method", e);
473 }
474 }
475 });
476 }
477
478 @VisibleForTesting
479 public void setOnDestroyedCallbackForTesting(Runnable onDestroyedCallbackFor Testing) {
480 mOnDestroyedCallbackForTesting = onDestroyedCallbackForTesting;
481 }
482
483 /**
484 * Posts task to application Executor. Used for callbacks
485 * and other tasks that should not be executed on network thread.
486 */
487 private void postTaskToExecutor(Runnable task) {
488 try {
489 mExecutor.execute(task);
490 } catch (RejectedExecutionException failException) {
491 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception posting task to ex ecutor",
492 failException);
493 // If posting a task throws an exception, then there is no choice
494 // but to destroy the stream without invoking the callback.
495 synchronized (mNativeStreamLock) {
496 mReadState = mWriteState = State.ERROR;
497 destroyNativeStreamLocked(false);
498 }
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(List<Map.Entry<String, String> > headersList) {
513 String headersArray[] = new String[headersList.size() * 2];
514 int i = 0;
515 for (Map.Entry<String, String> requestHeader : headersList) {
516 headersArray[i++] = requestHeader.getKey();
517 headersArray[i++] = requestHeader.getValue();
518 }
519 return headersArray;
520 }
521
522 private UrlResponseInfo prepareResponseInfoOnNetworkThread(
523 int httpStatusCode, String negotiatedProtocol, String[] headers) {
524 synchronized (mNativeStreamLock) {
525 if (mNativeStream == 0) {
526 return null;
527 }
528 }
529
530 ArrayList<String> urlChain = new ArrayList<String>();
531 urlChain.add(mInitialUrl);
532
533 boolean wasCached = false;
534 String httpStatusText = "";
535 String proxyServer = null;
536
537 UrlResponseInfo responseInfo = new UrlResponseInfo(urlChain, httpStatusC ode, httpStatusText,
538 headersListFromStrings(headers), wasCached, negotiatedProtocol, proxyServer);
539 return responseInfo;
540 }
541
542 private static int convertStreamPriority(
543 @BidirectionalStream.Builder.StreamPriority int priority) {
544 switch (priority) {
545 case Builder.STREAM_PRIORITY_IDLE:
546 return RequestPriority.IDLE;
547 case Builder.STREAM_PRIORITY_LOWEST:
548 return RequestPriority.LOWEST;
549 case Builder.STREAM_PRIORITY_LOW:
550 return RequestPriority.LOW;
551 case Builder.STREAM_PRIORITY_MEDIUM:
552 return RequestPriority.MEDIUM;
553 case Builder.STREAM_PRIORITY_HIGHEST:
554 return RequestPriority.HIGHEST;
555 default:
556 throw new IllegalArgumentException("Invalid stream priority.");
557 }
558 }
559
560 @GuardedBy("mNativeStreamLock")
561 private void destroyNativeStreamLocked(boolean sendOnCanceled) {
xunjieli 2016/01/21 19:13:08 Having the "Locked" suffix is confusing. Suggest g
pauljensen 2016/01/22 03:55:31 I thought the "Locked" suffix was a common pattern
xunjieli 2016/01/22 13:45:07 I see. Learned of it for the first time. SGTM then
mef 2016/01/22 14:33:44 Acknowledged.
562 Log.i(CronetUrlRequestContext.LOG_TAG, "destroyNativeStreamLocked " + th is.toString());
563 if (mNativeStream == 0) {
564 return;
565 }
566 nativeDestroy(mNativeStream, sendOnCanceled);
567 mNativeStream = 0;
568 mRequestContext.onRequestDestroyed();
569 if (mOnDestroyedCallbackForTesting != null) {
570 mOnDestroyedCallbackForTesting.run();
571 }
572 }
573
574 /**
575 * Fails the stream with an exception. Only called on the Executor.
576 */
577 private void failWithExceptionOnExecutor(CronetException e) {
578 // Do not call into listener if request is complete.
579 synchronized (mNativeStreamLock) {
580 if (isDoneLocked()) {
581 return;
582 }
583 mReadState = mWriteState = State.ERROR;
584 destroyNativeStreamLocked(false);
585 }
586 try {
587 mCallback.onFailed(this, mResponseInfo, e);
588 } catch (Exception failException) {
589 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception notifying of faile d request",
590 failException);
591 }
592 }
593
594 /**
595 * If callback method throws an exception, stream gets canceled
596 * and exception is reported via onFailed callback.
597 * Only called on the Executor.
598 */
599 private void onCallbackException(Exception e) {
600 CronetException streamError =
601 new CronetException("CalledByNative method has thrown an excepti on", e);
602 Log.e(CronetUrlRequestContext.LOG_TAG, "Exception in CalledByNative meth od", e);
603 failWithExceptionOnExecutor(streamError);
604 }
605
606 /**
607 * Fails the stream with an exception. Can be called on any thread.
608 */
609 private void failWithException(final CronetException exception) {
610 postTaskToExecutor(new Runnable() {
611 public void run() {
612 failWithExceptionOnExecutor(exception);
613 }
614 });
615 }
616
617 // Native methods are implemented in cronet_bidirectional_stream_adapter.cc.
618 private native long nativeCreateBidirectionalStream(long urlRequestContextAd apter);
619
620 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
621 private native int nativeStart(long nativePtr, String url, int priority, Str ing method,
622 String[] headers, boolean endOfStream);
623
624 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
625 private native boolean nativeReadData(
626 long nativePtr, ByteBuffer byteBuffer, int position, int limit);
627
628 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
629 private native boolean nativeWriteData(
630 long nativePtr, ByteBuffer byteBuffer, int position, int limit, bool ean endOfStream);
631
632 @NativeClassQualifiedName("CronetBidirectionalStreamAdapter")
633 private native void nativeDestroy(long nativePtr, boolean sendOnCanceled);
634 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698