1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <iterator>
18 
19 #include <dlfcn.h>
20 #include <jni.h>
21 
22 #include <android/log.h>
23 #include <nativehelper/jni_macros.h>
24 #include <nativehelper/scoped_local_ref.h>
25 #include <nativehelper/scoped_string_chars.h>
26 #include <nativehelper/scoped_utf_chars.h>
27 #include <nativehelper/JNIPlatformHelp.h>
28 
29 #include "libnativehelper_test.h"
30 
31 extern "C" int jniGetFDFromFileDescriptor_QR(JNIEnv* env, jobject fileDescriptor);
32 
33 namespace {
34 
AssertionError(JNIEnv * env,const char * format,...)35 void AssertionError(JNIEnv* env, const char* format, ...) {
36     // There is some awkwardness with java/lang/AssertionError. It cannot be directly thrown
37     // by jniThrowException or the JNI ThrowNew methods because AssertionError(String) is not
38     // public so we instead use the AssertionError(String, Throwable) constructor.
39     va_list args;
40     va_start(args, format);
41     char messageBuffer[256];
42     vsnprintf(messageBuffer, sizeof(messageBuffer), format, args);
43     va_end(args);
44 
45     ScopedLocalRef<jclass> jlaeClass(env, env->FindClass("java/lang/AssertionError"));
46     jmethodID init =
47         env->GetMethodID(jlaeClass.get(), "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
48     ScopedLocalRef<jstring> message(env, env->NewStringUTF(messageBuffer));
49     jobject jlae = env->NewObject(jlaeClass.get(), init, message.get(), nullptr);
50     env->Throw(reinterpret_cast<jthrowable>(jlae));
51 }
52 
throwException(JNIEnv * env,jclass,jstring className,jstring message)53 static void throwException(JNIEnv* env, jclass /*clazz*/, jstring className, jstring message) {
54     ScopedUtfChars c(env, className);
55     ScopedUtfChars m(env, message);
56     jniThrowException(env, c.c_str(), m.c_str());
57 }
58 
throwExceptionWithIntFormat(JNIEnv * env,jclass,jstring className,jstring format,jint value)59 static void throwExceptionWithIntFormat(JNIEnv* env,
60                                         jclass /*clazz*/,
61                                         jstring className,
62                                         jstring format,
63                                         jint value) {
64     ScopedUtfChars c(env, className);
65     ScopedUtfChars f(env, format);
66     jniThrowExceptionFmt(env, c.c_str(), f.c_str(), value);
67 }
68 
throwNullPointerException(JNIEnv * env,jclass,jstring message)69 static void throwNullPointerException(JNIEnv* env,
70                                       jclass /*clazz*/,
71                                       jstring message) {
72     ScopedUtfChars m(env, message);
73     jniThrowNullPointerException(env, m.c_str());
74 }
75 
throwRuntimeException(JNIEnv * env,jclass,jstring message)76 static void throwRuntimeException(JNIEnv* env, jclass /*clazz*/, jstring message) {
77     ScopedUtfChars m(env, message);
78     jniThrowRuntimeException(env, m.c_str());
79 }
80 
throwIOException(JNIEnv * env,jclass,jint cause)81 static void throwIOException(JNIEnv* env, jclass /*clazz*/, jint cause) {
82     jniThrowIOException(env, cause);
83 }
84 
logException(JNIEnv * env,jclass,jthrowable throwable)85 static void logException(JNIEnv* env, jclass /*clazz*/, jthrowable throwable) {
86     jniLogException(env, ANDROID_LOG_VERBOSE, __FILE__, throwable);
87 }
88 
fileDescriptorCreate(JNIEnv * env,jclass,jint unix_fd)89 static jobject fileDescriptorCreate(JNIEnv* env, jclass /*clazz*/, jint unix_fd) {
90     return jniCreateFileDescriptor(env, unix_fd);
91 }
92 
fileDescriptorGetFD(JNIEnv * env,jclass,jobject jiofd)93 static jint fileDescriptorGetFD(JNIEnv* env, jclass /*clazz*/, jobject jiofd) {
94     return jniGetFDFromFileDescriptor(env, jiofd);
95 }
96 
fileDescriptorGetFDCompatQR(JNIEnv * env,jclass,jobject jiofd)97 static jint fileDescriptorGetFDCompatQR(JNIEnv* env, jclass /*clazz*/, jobject jiofd) {
98     // Compatibility version of jniGetFDFromFileDescriptor for NetworkStack and Tethering modules
99     // that need to run on Android versions back to Android Q and Android R. The symbol is exported
100     // by libnativehelper_compat_libc++.so.
101     return jniGetFDFromFileDescriptor_QR(env, jiofd);
102 }
103 
fileDescriptorSetFD(JNIEnv * env,jclass,jobject jiofd,jint unix_fd)104 static void fileDescriptorSetFD(JNIEnv* env, jclass /*clazz*/, jobject jiofd, jint unix_fd) {
105     jniSetFileDescriptorOfFD(env, jiofd, unix_fd);
106 }
107 
allocateDirectNonHeapBuffer(JNIEnv * env,jclass,jint length)108 static jobject allocateDirectNonHeapBuffer(JNIEnv* env, jclass /*clazz*/, jint length) {
109     static uint8_t raw_memory[4096];
110     if (length < 0 || length > sizeof(raw_memory)) {
111         return nullptr;
112     }
113     return env->NewDirectByteBuffer(&raw_memory, length);
114 }
115 
116 static void
assertBufferBaseArrayOffsetBytes(JNIEnv * env,jclass,jobject jnb,jint offset)117 assertBufferBaseArrayOffsetBytes(JNIEnv* env, jclass /*clazz*/, jobject jnb, jint offset) {
118     int actualOffset = jniGetNioBufferBaseArrayOffset(env, jnb);
119     if (actualOffset != offset) {
120         AssertionError(env,
121                        "Buffer offset incorrect (expected %d, actual %d)",
122                        offset,
123                        actualOffset);
124     }
125 }
126 
assertBufferPosition(JNIEnv * env,jclass,jobject jnb,jint position)127 static void assertBufferPosition(JNIEnv* env, jclass /*clazz*/, jobject jnb, jint position) {
128     int actualPosition, actualLimit, actualElementSizeShift;
129     jniGetNioBufferFields(env, jnb, &actualPosition, &actualLimit, &actualElementSizeShift);
130     if (actualPosition != position) {
131         AssertionError(env,
132                        "Buffer position incorrect (expected %d, actual %d)",
133                        position,
134                        actualPosition);
135     }
136 }
137 
assertBufferLimit(JNIEnv * env,jclass,jobject jnb,jint limit)138 static void assertBufferLimit(JNIEnv* env, jclass /*clazz*/, jobject jnb, jint limit) {
139     int actualPosition, actualLimit, actualElementSizeShift;
140     jniGetNioBufferFields(env, jnb, &actualPosition, &actualLimit, &actualElementSizeShift);
141     if (actualLimit != limit) {
142         AssertionError(env,
143                        "Buffer limit incorrect (expected %d, actual %d)",
144                        limit,
145                        actualLimit);
146     }
147 }
148 
assertBufferElementSizeShift(JNIEnv * env,jclass,jobject jnb,jint ess)149 static void assertBufferElementSizeShift(JNIEnv* env, jclass /*clazz*/, jobject jnb, jint ess) {
150     int actualPosition, actualLimit, actualElementSizeShift;
151     jniGetNioBufferFields(env, jnb, &actualPosition, &actualLimit, &actualElementSizeShift);
152     if (actualElementSizeShift != ess) {
153         AssertionError(env,
154                        "Buffer element size shift incorrect (expected %d, actual %d)",
155                        ess,
156                        actualElementSizeShift);
157     }
158 }
159 
getBufferBaseAddress(JNIEnv * env,jclass,jobject jnb)160 static jlong getBufferBaseAddress(JNIEnv* env, jclass /*clazz*/, jobject jnb) {
161     int actualPosition, actualLimit, actualElementSizeShift;
162     return jniGetNioBufferFields(env, jnb, &actualPosition, &actualLimit, &actualElementSizeShift);
163 }
164 
getDirectBufferAddress(JNIEnv * env,jclass,jobject jnb)165 static jlong getDirectBufferAddress(JNIEnv* env, jclass /*clazz*/, jobject jnb) {
166     void* directBufferAddress = env->GetDirectBufferAddress(jnb);
167     return reinterpret_cast<jlong>(directBufferAddress);
168 }
169 
assertBufferPointer(JNIEnv * env,jclass,jobject jnb,jlong pointer)170 static void assertBufferPointer(JNIEnv* env, jclass /*clazz*/, jobject jnb, jlong pointer) {
171     jlong actualPointer = jniGetNioBufferPointer(env, jnb);
172     if (actualPointer != pointer) {
173         AssertionError(env,
174                       "Buffer pointer incorrect (expected %p, actual %p)",
175                        reinterpret_cast<void*>(pointer),
176                        reinterpret_cast<void*>(actualPointer));
177     }
178 }
179 
getReferent(JNIEnv * env,jclass,jobject reference)180 static jobject getReferent(JNIEnv* env, jclass /*clazz*/, jobject reference) {
181     return jniGetReferent(env, reference);
182 }
183 
createString(JNIEnv * env,jclass,jstring value)184 static jstring createString(JNIEnv* env, jclass /*clazz*/, jstring value) {
185     ScopedStringChars ssc(env, value);
186     return jniCreateString(env, ssc.get(), ssc.size());
187 }
188 
createStringArray(JNIEnv * env,jclass,jint length)189 static jobjectArray createStringArray(JNIEnv* env, jclass /*clazz*/, jint length) {
190     return jniCreateStringArray(&env->functions, length);
191 }
192 
193 }  // namespace
194 
JNI_OnLoad(JavaVM * vm,void * reserved)195 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
196     JNIEnv* env;
197     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
198         return JNI_ERR;
199     }
200 
201     static const JNINativeMethod methods[] = {
202         MAKE_JNI_NATIVE_METHOD("throwException",
203                                "(Ljava/lang/String;Ljava/lang/String;)V",
204                                throwException),
205         MAKE_JNI_NATIVE_METHOD("throwExceptionWithIntFormat",
206                                "(Ljava/lang/String;Ljava/lang/String;I)V",
207                                throwExceptionWithIntFormat),
208         MAKE_JNI_NATIVE_METHOD("throwNullPointerException",
209                                "(Ljava/lang/String;)V",
210                                throwNullPointerException),
211         MAKE_JNI_NATIVE_METHOD("throwRuntimeException",
212                                "(Ljava/lang/String;)V",
213                                throwRuntimeException),
214         MAKE_JNI_NATIVE_METHOD("throwIOException",
215                                "(I)V",
216                                throwIOException),
217         MAKE_JNI_NATIVE_METHOD("logException",
218                                "(Ljava/lang/Throwable;)V",
219                                logException),
220         MAKE_JNI_NATIVE_METHOD("fileDescriptorCreate",
221                                "(I)Ljava/io/FileDescriptor;",
222                                fileDescriptorCreate),
223         MAKE_JNI_NATIVE_METHOD("fileDescriptorGetFD",
224                                "(Ljava/io/FileDescriptor;)I",
225                                fileDescriptorGetFD),
226         MAKE_JNI_NATIVE_METHOD("fileDescriptorGetFDCompatQR",
227                                "(Ljava/io/FileDescriptor;)I",
228                                fileDescriptorGetFDCompatQR),
229         MAKE_JNI_NATIVE_METHOD("fileDescriptorSetFD",
230                                "(Ljava/io/FileDescriptor;I)V",
231                                fileDescriptorSetFD),
232         MAKE_JNI_NATIVE_METHOD("allocateDirectNonHeapBuffer",
233                                "(I)Ljava/nio/ByteBuffer;",
234                                allocateDirectNonHeapBuffer),
235         MAKE_JNI_NATIVE_METHOD("assertBufferBaseArrayOffsetBytes",
236                                "(Ljava/nio/Buffer;I)V",
237                                assertBufferBaseArrayOffsetBytes),
238         MAKE_JNI_NATIVE_METHOD("assertBufferPosition",
239                                "(Ljava/nio/Buffer;I)V",
240                                assertBufferPosition),
241         MAKE_JNI_NATIVE_METHOD("assertBufferLimit",
242                                "(Ljava/nio/Buffer;I)V",
243                                assertBufferLimit),
244         MAKE_JNI_NATIVE_METHOD("assertBufferElementSizeShift",
245                                "(Ljava/nio/Buffer;I)V",
246                                assertBufferElementSizeShift),
247         MAKE_JNI_NATIVE_METHOD("getBufferBaseAddress",
248                                "(Ljava/nio/Buffer;)J",
249                                getBufferBaseAddress),
250         MAKE_JNI_NATIVE_METHOD("getDirectBufferAddress",
251                                "(Ljava/nio/Buffer;)J",
252                                getDirectBufferAddress),
253         MAKE_JNI_NATIVE_METHOD("assertBufferPointer",
254                                "(Ljava/nio/Buffer;J)V",
255                                assertBufferPointer),
256         MAKE_JNI_NATIVE_METHOD("getReferent",
257                                "(Ljava/lang/ref/Reference;)Ljava/lang/Object;",
258                                getReferent),
259         MAKE_JNI_NATIVE_METHOD("createString",
260                                "(Ljava/lang/String;)Ljava/lang/String;",
261                                createString),
262         MAKE_JNI_NATIVE_METHOD("createStringArray",
263                                "(I)[Ljava/lang/String;",
264                                createStringArray)
265     };
266     int rc = jniRegisterNativeMethods(env,
267                                       "android/libnativehelper/mts/JniHelpTest",
268                                       methods,
269                                       std::size(methods));
270     if (rc != JNI_OK) return rc;
271 
272     return JNI_VERSION_1_6;
273 }
274