// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/android/jni_array.h" #include #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/check_op.h" #include "base/numerics/safe_conversions.h" namespace base { namespace android { namespace { // As |GetArrayLength| makes no guarantees about the returned value (e.g., it // may be -1 if |array| is not a valid Java array), provide a safe wrapper // that always returns a valid, non-negative size. template size_t SafeGetArrayLength(JNIEnv* env, const JavaRef& jarray) { DCHECK(jarray); jsize length = env->GetArrayLength(jarray.obj()); DCHECK_GE(length, 0) << "Invalid array length: " << length; return static_cast(std::max(0, length)); } } // namespace ScopedJavaLocalRef ToJavaByteArray(JNIEnv* env, const uint8_t* bytes, size_t len) { const jsize len_jsize = checked_cast(len); jbyteArray byte_array = env->NewByteArray(len_jsize); CheckException(env); DCHECK(byte_array); env->SetByteArrayRegion(byte_array, 0, len_jsize, reinterpret_cast(bytes)); CheckException(env); return ScopedJavaLocalRef(env, byte_array); } ScopedJavaLocalRef ToJavaByteArray( JNIEnv* env, base::span bytes) { return ToJavaByteArray(env, bytes.data(), bytes.size()); } ScopedJavaLocalRef ToJavaByteArray(JNIEnv* env, const std::string& str) { return ToJavaByteArray(env, reinterpret_cast(str.data()), str.size()); } ScopedJavaLocalRef ToJavaBooleanArray(JNIEnv* env, const bool* bools, size_t len) { const jsize len_jsize = checked_cast(len); jbooleanArray boolean_array = env->NewBooleanArray(len_jsize); CheckException(env); DCHECK(boolean_array); env->SetBooleanArrayRegion(boolean_array, 0, len_jsize, reinterpret_cast(bools)); CheckException(env); return ScopedJavaLocalRef(env, boolean_array); } ScopedJavaLocalRef ToJavaIntArray(JNIEnv* env, const int* ints, size_t len) { const jsize len_jsize = checked_cast(len); jintArray int_array = env->NewIntArray(len_jsize); CheckException(env); DCHECK(int_array); env->SetIntArrayRegion(int_array, 0, len_jsize, reinterpret_cast(ints)); CheckException(env); return ScopedJavaLocalRef(env, int_array); } ScopedJavaLocalRef ToJavaIntArray(JNIEnv* env, base::span ints) { return ToJavaIntArray(env, ints.data(), ints.size()); } ScopedJavaLocalRef ToJavaLongArray(JNIEnv* env, const int64_t* longs, size_t len) { const jsize len_jsize = checked_cast(len); jlongArray long_array = env->NewLongArray(len_jsize); CheckException(env); DCHECK(long_array); env->SetLongArrayRegion(long_array, 0, len_jsize, reinterpret_cast(longs)); CheckException(env); return ScopedJavaLocalRef(env, long_array); } // Returns a new Java long array converted from the given int64_t array. BASE_EXPORT ScopedJavaLocalRef ToJavaLongArray( JNIEnv* env, base::span longs) { return ToJavaLongArray(env, longs.data(), longs.size()); } // Returns a new Java float array converted from the given C++ float array. BASE_EXPORT ScopedJavaLocalRef ToJavaFloatArray(JNIEnv* env, const float* floats, size_t len) { const jsize len_jsize = checked_cast(len); jfloatArray float_array = env->NewFloatArray(len_jsize); CheckException(env); DCHECK(float_array); env->SetFloatArrayRegion(float_array, 0, len_jsize, reinterpret_cast(floats)); CheckException(env); return ScopedJavaLocalRef(env, float_array); } BASE_EXPORT ScopedJavaLocalRef ToJavaFloatArray( JNIEnv* env, base::span floats) { return ToJavaFloatArray(env, floats.data(), floats.size()); } BASE_EXPORT ScopedJavaLocalRef ToJavaDoubleArray(JNIEnv* env, const double* doubles, size_t len) { const jsize len_jsize = checked_cast(len); jdoubleArray double_array = env->NewDoubleArray(len_jsize); CheckException(env); DCHECK(double_array); env->SetDoubleArrayRegion(double_array, 0, len_jsize, reinterpret_cast(doubles)); CheckException(env); return ScopedJavaLocalRef(env, double_array); } BASE_EXPORT ScopedJavaLocalRef ToJavaDoubleArray( JNIEnv* env, base::span doubles) { return ToJavaDoubleArray(env, doubles.data(), doubles.size()); } BASE_EXPORT ScopedJavaLocalRef ToJavaArrayOfObjects( JNIEnv* env, ScopedJavaLocalRef clazz, base::span> v) { jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { env->SetObjectArrayElement(joa, static_cast(i), v[i].obj()); } return ScopedJavaLocalRef(env, joa); } BASE_EXPORT ScopedJavaLocalRef ToJavaArrayOfObjects( JNIEnv* env, base::span> v) { return ToJavaArrayOfObjects(env, GetClass(env, "java/lang/Object"), v); } BASE_EXPORT ScopedJavaLocalRef ToJavaArrayOfObjects( JNIEnv* env, base::span> v) { ScopedJavaLocalRef object_array_clazz = GetClass(env, "java/lang/Object"); jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), object_array_clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { env->SetObjectArrayElement(joa, static_cast(i), v[i].obj()); } return ScopedJavaLocalRef(env, joa); } BASE_EXPORT ScopedJavaLocalRef ToTypedJavaArrayOfObjects( JNIEnv* env, base::span> v, ScopedJavaLocalRef type) { jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), type.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { env->SetObjectArrayElement(joa, static_cast(i), v[i].obj()); } return ScopedJavaLocalRef(env, joa); } BASE_EXPORT ScopedJavaLocalRef ToTypedJavaArrayOfObjects( JNIEnv* env, base::span> v, ScopedJavaLocalRef type) { jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), type.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { env->SetObjectArrayElement(joa, static_cast(i), v[i].obj()); } return ScopedJavaLocalRef(env, joa); } ScopedJavaLocalRef ToJavaArrayOfByteArray( JNIEnv* env, base::span v) { ScopedJavaLocalRef byte_array_clazz = GetClass(env, "[B"); jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), byte_array_clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { ScopedJavaLocalRef byte_array = ToJavaByteArray( env, reinterpret_cast(v[i].data()), v[i].length()); env->SetObjectArrayElement(joa, static_cast(i), byte_array.obj()); } return ScopedJavaLocalRef(env, joa); } ScopedJavaLocalRef ToJavaArrayOfByteArray( JNIEnv* env, base::span> v) { ScopedJavaLocalRef byte_array_clazz = GetClass(env, "[B"); jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), byte_array_clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { ScopedJavaLocalRef byte_array = ToJavaByteArray(env, v[i].data(), v[i].size()); env->SetObjectArrayElement(joa, static_cast(i), byte_array.obj()); } return ScopedJavaLocalRef(env, joa); } ScopedJavaLocalRef ToJavaArrayOfStrings( JNIEnv* env, base::span v) { ScopedJavaLocalRef string_clazz = GetClass(env, "java/lang/String"); jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), string_clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { ScopedJavaLocalRef item = ConvertUTF8ToJavaString(env, v[i]); env->SetObjectArrayElement(joa, static_cast(i), item.obj()); } return ScopedJavaLocalRef(env, joa); } ScopedJavaLocalRef ToJavaArrayOfStringArray( JNIEnv* env, base::span> vec_outer) { ScopedJavaLocalRef string_array_clazz = GetClass(env, "[Ljava/lang/String;"); jobjectArray joa = env->NewObjectArray(checked_cast(vec_outer.size()), string_array_clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < vec_outer.size(); ++i) { ScopedJavaLocalRef inner = ToJavaArrayOfStrings(env, vec_outer[i]); env->SetObjectArrayElement(joa, static_cast(i), inner.obj()); } return ScopedJavaLocalRef(env, joa); } ScopedJavaLocalRef ToJavaArrayOfStringArray( JNIEnv* env, base::span> vec_outer) { ScopedJavaLocalRef string_array_clazz = GetClass(env, "[Ljava/lang/String;"); jobjectArray joa = env->NewObjectArray(checked_cast(vec_outer.size()), string_array_clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < vec_outer.size(); ++i) { ScopedJavaLocalRef inner = ToJavaArrayOfStrings(env, vec_outer[i]); env->SetObjectArrayElement(joa, static_cast(i), inner.obj()); } return ScopedJavaLocalRef(env, joa); } ScopedJavaLocalRef ToJavaArrayOfStrings( JNIEnv* env, base::span v) { ScopedJavaLocalRef string_clazz = GetClass(env, "java/lang/String"); jobjectArray joa = env->NewObjectArray(checked_cast(v.size()), string_clazz.obj(), nullptr); CheckException(env); for (size_t i = 0; i < v.size(); ++i) { ScopedJavaLocalRef item = ConvertUTF16ToJavaString(env, v[i]); env->SetObjectArrayElement(joa, static_cast(i), item.obj()); } return ScopedJavaLocalRef(env, joa); } void AppendJavaStringArrayToStringVector(JNIEnv* env, const JavaRef& array, std::vector* out) { DCHECK(out); if (!array) return; size_t len = SafeGetArrayLength(env, array); size_t back = out->size(); out->resize(back + len); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef str( env, static_cast(env->GetObjectArrayElement( array.obj(), static_cast(i)))); ConvertJavaStringToUTF16(env, str.obj(), out->data() + back + i); } } void AppendJavaStringArrayToStringVector(JNIEnv* env, const JavaRef& array, std::vector* out) { DCHECK(out); if (!array) return; size_t len = SafeGetArrayLength(env, array); size_t back = out->size(); out->resize(back + len); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef str( env, static_cast(env->GetObjectArrayElement( array.obj(), static_cast(i)))); ConvertJavaStringToUTF8(env, str.obj(), out->data() + back + i); } } void AppendJavaByteArrayToByteVector(JNIEnv* env, const JavaRef& byte_array, std::vector* out) { DCHECK(out); if (!byte_array) return; size_t len = SafeGetArrayLength(env, byte_array); if (!len) return; size_t back = out->size(); out->resize(back + len); env->GetByteArrayRegion(byte_array.obj(), 0, static_cast(len), reinterpret_cast(out->data() + back)); } void JavaByteArrayToByteVector(JNIEnv* env, const JavaRef& byte_array, std::vector* out) { DCHECK(out); DCHECK(byte_array); out->clear(); AppendJavaByteArrayToByteVector(env, byte_array, out); } void JavaByteArrayToString(JNIEnv* env, const JavaRef& byte_array, std::string* out) { DCHECK(out); DCHECK(byte_array); std::vector byte_vector; JavaByteArrayToByteVector(env, byte_array, &byte_vector); out->assign(byte_vector.begin(), byte_vector.end()); } void JavaBooleanArrayToBoolVector(JNIEnv* env, const JavaRef& boolean_array, std::vector* out) { DCHECK(out); if (!boolean_array) return; size_t len = SafeGetArrayLength(env, boolean_array); if (!len) return; out->resize(len); // It is not possible to get bool* out of vector. jboolean* values = env->GetBooleanArrayElements(boolean_array.obj(), nullptr); for (size_t i = 0; i < len; ++i) { out->at(i) = static_cast(values[i]); } env->ReleaseBooleanArrayElements(boolean_array.obj(), values, JNI_ABORT); } void JavaIntArrayToIntVector(JNIEnv* env, const JavaRef& int_array, std::vector* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, int_array); out->resize(len); if (!len) return; env->GetIntArrayRegion(int_array.obj(), 0, static_cast(len), out->data()); } void JavaLongArrayToInt64Vector(JNIEnv* env, const JavaRef& long_array, std::vector* out) { DCHECK(out); std::vector temp; JavaLongArrayToLongVector(env, long_array, &temp); out->resize(0); out->insert(out->begin(), temp.begin(), temp.end()); } void JavaLongArrayToLongVector(JNIEnv* env, const JavaRef& long_array, std::vector* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, long_array); out->resize(len); if (!len) return; env->GetLongArrayRegion(long_array.obj(), 0, static_cast(len), out->data()); } void JavaFloatArrayToFloatVector(JNIEnv* env, const JavaRef& float_array, std::vector* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, float_array); out->resize(len); if (!len) return; env->GetFloatArrayRegion(float_array.obj(), 0, static_cast(len), out->data()); } void JavaDoubleArrayToDoubleVector(JNIEnv* env, const JavaRef& double_array, std::vector* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, double_array); out->resize(len); if (!len) return; env->GetDoubleArrayRegion(double_array.obj(), 0, static_cast(len), out->data()); } void JavaArrayOfByteArrayToStringVector(JNIEnv* env, const JavaRef& array, std::vector* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, array); out->resize(len); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef bytes_array( env, static_cast(env->GetObjectArrayElement( array.obj(), static_cast(i)))); size_t bytes_len = SafeGetArrayLength(env, bytes_array); jbyte* bytes = env->GetByteArrayElements(bytes_array.obj(), nullptr); (*out)[i].assign(reinterpret_cast(bytes), bytes_len); env->ReleaseByteArrayElements(bytes_array.obj(), bytes, JNI_ABORT); } } void JavaArrayOfByteArrayToBytesVector(JNIEnv* env, const JavaRef& array, std::vector>* out) { DCHECK(out); const size_t len = SafeGetArrayLength(env, array); out->resize(len); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef bytes_array( env, static_cast(env->GetObjectArrayElement( array.obj(), static_cast(i)))); JavaByteArrayToByteVector(env, bytes_array, &(*out)[i]); } } void Java2dStringArrayTo2dStringVector( JNIEnv* env, const JavaRef& array, std::vector>* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, array); out->resize(len); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef strings_array( env, static_cast(env->GetObjectArrayElement( array.obj(), static_cast(i)))); out->at(i).clear(); AppendJavaStringArrayToStringVector(env, strings_array, &out->at(i)); } } void Java2dStringArrayTo2dStringVector( JNIEnv* env, const JavaRef& array, std::vector>* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, array); out->resize(len); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef strings_array( env, static_cast(env->GetObjectArrayElement( array.obj(), static_cast(i)))); out->at(i).clear(); AppendJavaStringArrayToStringVector(env, strings_array, &out->at(i)); } } void JavaArrayOfIntArrayToIntVector(JNIEnv* env, const JavaRef& array, std::vector>* out) { DCHECK(out); size_t len = SafeGetArrayLength(env, array); out->resize(len); for (size_t i = 0; i < len; ++i) { ScopedJavaLocalRef int_array( env, static_cast(env->GetObjectArrayElement( array.obj(), static_cast(i)))); JavaIntArrayToIntVector(env, int_array, &out->at(i)); } } } // namespace android } // namespace base