[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 1 | // Copyright (c) 2012 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 | #include "android_webview/native/input_stream_impl.h" |
| 6 | |
| 7 | #include "base/android/jni_android.h" |
| 8 | // Disable "Warnings treated as errors" for input_stream_jni as it's a Java |
| 9 | // system class and we have to generate C++ hooks for all methods in the class |
| 10 | // even if they're unused. |
| 11 | #pragma GCC diagnostic push |
| 12 | #pragma GCC diagnostic ignored "-Wunused-function" |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 13 | #include "jni/InputStreamUtil_jni.h" |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 14 | #pragma GCC diagnostic pop |
| 15 | #include "net/base/io_buffer.h" |
| 16 | |
| 17 | using base::android::AttachCurrentThread; |
| 18 | using base::android::ClearException; |
| 19 | using base::android::JavaRef; |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 20 | |
| 21 | namespace android_webview { |
| 22 | |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 23 | namespace { |
| 24 | |
| 25 | // This should be the same as InputStramUtil.EXCEPTION_THROWN_STATUS. |
| 26 | const int kExceptionThrownStatusCode = -2; |
| 27 | |
| 28 | } |
| 29 | |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 30 | bool RegisterInputStream(JNIEnv* env) { |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 31 | return RegisterNativesImpl(env); |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 32 | } |
| 33 | |
| 34 | // Maximum number of bytes to be read in a single read. |
| 35 | const int InputStreamImpl::kBufferSize = 4096; |
| 36 | |
| 37 | //static |
| 38 | const InputStreamImpl* InputStreamImpl::FromInputStream( |
| 39 | const InputStream* input_stream) { |
| 40 | return static_cast<const InputStreamImpl*>(input_stream); |
| 41 | } |
| 42 | |
[email protected] | 8c6b319 | 2013-01-04 18:43:47 | [diff] [blame] | 43 | // TODO: Use unsafe version for all Java_InputStream methods in this file |
| 44 | // once BUG 157880 is fixed and implement graceful exception handling. |
| 45 | |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 46 | InputStreamImpl::InputStreamImpl() { |
| 47 | } |
| 48 | |
| 49 | InputStreamImpl::InputStreamImpl(const JavaRef<jobject>& stream) |
| 50 | : jobject_(stream) { |
| 51 | DCHECK(!stream.is_null()); |
| 52 | } |
| 53 | |
| 54 | InputStreamImpl::~InputStreamImpl() { |
[email protected] | 0a6d8e2 | 2013-01-03 16:26:39 | [diff] [blame] | 55 | JNIEnv* env = AttachCurrentThread(); |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 56 | Java_InputStreamUtil_close(env, jobject_.obj()); |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | bool InputStreamImpl::BytesAvailable(int* bytes_available) const { |
| 60 | JNIEnv* env = AttachCurrentThread(); |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 61 | int bytes = Java_InputStreamUtil_available(env, jobject_.obj()); |
| 62 | if (bytes == kExceptionThrownStatusCode) |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 63 | return false; |
| 64 | *bytes_available = bytes; |
| 65 | return true; |
| 66 | } |
| 67 | |
| 68 | bool InputStreamImpl::Skip(int64_t n, int64_t* bytes_skipped) { |
| 69 | JNIEnv* env = AttachCurrentThread(); |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 70 | int bytes = Java_InputStreamUtil_skip(env, jobject_.obj(), n); |
| 71 | if (bytes < 0) |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 72 | return false; |
| 73 | if (bytes > n) |
| 74 | return false; |
| 75 | *bytes_skipped = bytes; |
| 76 | return true; |
| 77 | } |
| 78 | |
| 79 | bool InputStreamImpl::Read(net::IOBuffer* dest, int length, int* bytes_read) { |
| 80 | JNIEnv* env = AttachCurrentThread(); |
| 81 | if (!buffer_.obj()) { |
| 82 | // Allocate transfer buffer. |
[email protected] | 8d1f3df | 2013-07-16 16:41:20 | [diff] [blame] | 83 | base::android::ScopedJavaLocalRef<jbyteArray> temp( |
| 84 | env, env->NewByteArray(kBufferSize)); |
| 85 | buffer_.Reset(temp); |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 86 | if (ClearException(env)) |
| 87 | return false; |
| 88 | } |
| 89 | |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 90 | int remaining_length = length; |
| 91 | char* dest_write_ptr = dest->data(); |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 92 | jbyteArray buffer = buffer_.obj(); |
| 93 | *bytes_read = 0; |
| 94 | |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 95 | while (remaining_length > 0) { |
| 96 | const int max_transfer_length = std::min(remaining_length, kBufferSize); |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 97 | const int transfer_length = Java_InputStreamUtil_read( |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 98 | env, jobject_.obj(), buffer, 0, max_transfer_length); |
[email protected] | 17fd3aa | 2014-07-29 16:35:42 | [diff] [blame] | 99 | if (transfer_length == kExceptionThrownStatusCode) |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 100 | return false; |
| 101 | |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 102 | if (transfer_length < 0) // EOF |
| 103 | break; |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 104 | |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 105 | // Note: it is possible, yet unlikely, that the Java InputStream returns |
| 106 | // a transfer_length == 0 from time to time. In such cases we just continue |
| 107 | // the read until we get either valid data or reach EOF. |
| 108 | if (transfer_length == 0) |
| 109 | continue; |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 110 | |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 111 | DCHECK_GE(max_transfer_length, transfer_length); |
| 112 | DCHECK_GE(env->GetArrayLength(buffer), transfer_length); |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 113 | |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 114 | // This check is to prevent a malicious InputStream implementation from |
| 115 | // overrunning the |dest| buffer. |
| 116 | if (transfer_length > max_transfer_length) |
| 117 | return false; |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 118 | |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 119 | // Copy the data over to the provided C++ IOBuffer. |
| 120 | DCHECK_GE(remaining_length, transfer_length); |
| 121 | env->GetByteArrayRegion(buffer, 0, transfer_length, |
| 122 | reinterpret_cast<jbyte*>(dest_write_ptr)); |
| 123 | if (ClearException(env)) |
| 124 | return false; |
| 125 | |
| 126 | remaining_length -= transfer_length; |
| 127 | dest_write_ptr += transfer_length; |
| 128 | } |
| 129 | // bytes_read can be strictly less than the req. length if EOF is encountered. |
boliu | 2d46b52 | 2014-08-29 22:33:44 | [diff] [blame] | 130 | DCHECK_GE(remaining_length, 0); |
| 131 | DCHECK_LE(remaining_length, length); |
[email protected] | 81959dd | 2013-09-19 06:17:48 | [diff] [blame] | 132 | *bytes_read = length - remaining_length; |
[email protected] | 1737386 | 2012-11-22 16:21:26 | [diff] [blame] | 133 | return true; |
| 134 | } |
| 135 | |
| 136 | } // namespace android_webview |