• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RegisterInputStream(JNIEnv * env)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
FromInputStream(const InputStream * input_stream)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 
InputStreamImpl()46 InputStreamImpl::InputStreamImpl() {
47 }
48 
InputStreamImpl(const JavaRef<jobject> & stream)49 InputStreamImpl::InputStreamImpl(const JavaRef<jobject>& stream)
50     : jobject_(stream) {
51   DCHECK(!stream.is_null());
52 }
53 
~InputStreamImpl()54 InputStreamImpl::~InputStreamImpl() {
55   JNIEnv* env = AttachCurrentThread();
56   Java_InputStreamUtil_close(env, jobject_.obj());
57 }
58 
BytesAvailable(int * bytes_available) const59 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 
Skip(int64_t n,int64_t * bytes_skipped)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 
Read(net::IOBuffer * dest,int length,int * bytes_read)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