File: input_stream.cc

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (151 lines) | stat: -rw-r--r-- 5,067 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "components/embedder_support/android/util/input_stream.h"

#include "base/android/jni_android.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
// Disable "Warnings treated as errors" for input_stream_jni as it's a Java
// system class and we have to generate C++ hooks for all methods in the class
// even if they're unused.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic pop
#include "net/base/io_buffer.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/embedder_support/android/util_jni_headers/InputStreamUtil_jni.h"

using base::android::AttachCurrentThread;
using base::android::ClearException;
using base::android::JavaRef;

namespace embedder_support {

namespace {

// This should be the same as InputStramUtil.EXCEPTION_THROWN_STATUS.
const int kExceptionThrownStatusCode = -2;
}  // namespace

// Experiment to control the size of the intermediate buffer used to copy from
// Java's InputStream into C++'s net::IOBuffer.
BASE_FEATURE(kEnableCustomInputStreamBufferSize,
             "EnableCustomInputStreamBufferSize",
             base::FEATURE_DISABLED_BY_DEFAULT);

// Effectively the maximum number of bytes that will be copied during a JNI call
// to Java_InputStreamUtil_read.
const base::FeatureParam<int> kBufferSize{&kEnableCustomInputStreamBufferSize,
                                          "BufferSize", 4096};

// static
int InputStream::GetIntermediateBufferSize() {
  return kBufferSize.Get();
}

// TODO: Use unsafe version for all Java_InputStream methods in this file
// once BUG 157880 is fixed and implement graceful exception handling.

InputStream::InputStream() = default;

InputStream::InputStream(const JavaRef<jobject>& stream) : jobject_(stream) {
  DCHECK(stream);
}

InputStream::~InputStream() {
  base::UmaHistogramCounts10000("Android.InputStream.TotalRead.SizeKB",
                                total_bytes_read_ / 1024);

  JNIEnv* env = AttachCurrentThread();
  if (jobject_.obj())
    Java_InputStreamUtil_close(env, jobject_);
}

bool InputStream::BytesAvailable(int* bytes_available) const {
  JNIEnv* env = AttachCurrentThread();
  int bytes = Java_InputStreamUtil_available(env, jobject_);
  if (bytes == kExceptionThrownStatusCode)
    return false;
  *bytes_available = bytes;
  return true;
}

bool InputStream::Skip(int64_t n, int64_t* bytes_skipped) {
  JNIEnv* env = AttachCurrentThread();
  int bytes = Java_InputStreamUtil_skip(env, jobject_, n);
  if (bytes < 0)
    return false;
  if (bytes > n)
    return false;
  *bytes_skipped = bytes;
  return true;
}

bool InputStream::Read(net::IOBuffer* dest, int length, int* bytes_read) {
  JNIEnv* env = AttachCurrentThread();
  if (!buffer_.obj()) {
    // Allocate transfer buffer.
    base::android::ScopedJavaLocalRef<jbyteArray> temp(
        env, env->NewByteArray(GetIntermediateBufferSize()));
    buffer_.Reset(temp);
    if (ClearException(env))
      return false;
  }

  int remaining_length = length;
  char* dest_write_ptr = dest->data();
  *bytes_read = 0;

  while (remaining_length > 0) {
    const int max_transfer_length =
        std::min(remaining_length, GetIntermediateBufferSize());
    const int transfer_length = Java_InputStreamUtil_read(
        env, jobject_, buffer_, 0, max_transfer_length);
    if (transfer_length == kExceptionThrownStatusCode)
      return false;

    if (transfer_length < 0)  // EOF
      break;

    // Note: it is possible, yet unlikely, that the Java InputStream returns
    // a transfer_length == 0 from time to time. In such cases we just continue
    // the read until we get either valid data or reach EOF.
    if (transfer_length == 0)
      continue;

    DCHECK_GE(max_transfer_length, transfer_length);
    DCHECK_GE(env->GetArrayLength(buffer_.obj()), transfer_length);

    // This check is to prevent a malicious InputStream implementation from
    // overrunning the |dest| buffer.
    if (transfer_length > max_transfer_length)
      return false;

    // Copy the data over to the provided C++ IOBuffer.
    DCHECK_GE(remaining_length, transfer_length);
    env->GetByteArrayRegion(buffer_.obj(), 0, transfer_length,
                            reinterpret_cast<jbyte*>(dest_write_ptr));
    if (ClearException(env))
      return false;

    remaining_length -= transfer_length;
    dest_write_ptr += transfer_length;
  }
  // bytes_read can be strictly less than the req. length if EOF is encountered.
  DCHECK_GE(remaining_length, 0);
  DCHECK_LE(remaining_length, length);
  *bytes_read = length - remaining_length;
  total_bytes_read_ += *bytes_read;
  return true;
}

}  // namespace embedder_support