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" 13 #include "jni/InputStreamUtil_jni.h" 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; 20 21 namespace android_webview { 22 23 namespace { 24 25 // This should be the same as InputStramUtil.EXCEPTION_THROWN_STATUS. 26 const int kExceptionThrownStatusCode = -2; 27 28 } 29 30 bool RegisterInputStream(JNIEnv* env) { 31 return RegisterNativesImpl(env); 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 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 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() { 55 JNIEnv* env = AttachCurrentThread(); 56 Java_InputStreamUtil_close(env, jobject_.obj()); 57 } 58 59 bool InputStreamImpl::BytesAvailable(int* bytes_available) const { 60 JNIEnv* env = AttachCurrentThread(); 61 int bytes = Java_InputStreamUtil_available(env, jobject_.obj()); 62 if (bytes == kExceptionThrownStatusCode) 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(); 70 int bytes = Java_InputStreamUtil_skip(env, jobject_.obj(), n); 71 if (bytes < 0) 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. 83 base::android::ScopedJavaLocalRef<jbyteArray> temp( 84 env, env->NewByteArray(kBufferSize)); 85 buffer_.Reset(temp); 86 if (ClearException(env)) 87 return false; 88 } 89 90 int remaining_length = length; 91 char* dest_write_ptr = dest->data(); 92 jbyteArray buffer = buffer_.obj(); 93 *bytes_read = 0; 94 95 while (remaining_length > 0) { 96 const int max_transfer_length = std::min(remaining_length, kBufferSize); 97 const int transfer_length = Java_InputStreamUtil_read( 98 env, jobject_.obj(), buffer, 0, max_transfer_length); 99 if (transfer_length == kExceptionThrownStatusCode) 100 return false; 101 102 if (transfer_length < 0) // EOF 103 break; 104 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; 110 111 DCHECK_GE(max_transfer_length, transfer_length); 112 DCHECK_GE(env->GetArrayLength(buffer), transfer_length); 113 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; 118 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. 130 DCHECK(remaining_length >= 0 && remaining_length <= length); 131 *bytes_read = length - remaining_length; 132 return true; 133 } 134 135 } // namespace android_webview 136