1*3c7ae9deSAndroid Build Coastguard Worker /**
2*3c7ae9deSAndroid Build Coastguard Worker * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3*3c7ae9deSAndroid Build Coastguard Worker * SPDX-License-Identifier: Apache-2.0.
4*3c7ae9deSAndroid Build Coastguard Worker */
5*3c7ae9deSAndroid Build Coastguard Worker
6*3c7ae9deSAndroid Build Coastguard Worker #include <aws/auth/auth.h>
7*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/allocator.h>
8*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/atomics.h>
9*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/clock.h>
10*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/common.h>
11*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/hash_table.h>
12*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/logging.h>
13*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/rw_lock.h>
14*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/string.h>
15*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/system_info.h>
16*3c7ae9deSAndroid Build Coastguard Worker #include <aws/common/thread.h>
17*3c7ae9deSAndroid Build Coastguard Worker #include <aws/event-stream/event_stream.h>
18*3c7ae9deSAndroid Build Coastguard Worker #include <aws/http/connection.h>
19*3c7ae9deSAndroid Build Coastguard Worker #include <aws/http/http.h>
20*3c7ae9deSAndroid Build Coastguard Worker #include <aws/io/channel.h>
21*3c7ae9deSAndroid Build Coastguard Worker #include <aws/io/io.h>
22*3c7ae9deSAndroid Build Coastguard Worker #include <aws/io/logging.h>
23*3c7ae9deSAndroid Build Coastguard Worker #include <aws/io/tls_channel_handler.h>
24*3c7ae9deSAndroid Build Coastguard Worker #include <aws/mqtt/mqtt.h>
25*3c7ae9deSAndroid Build Coastguard Worker #include <aws/s3/s3.h>
26*3c7ae9deSAndroid Build Coastguard Worker
27*3c7ae9deSAndroid Build Coastguard Worker #include <stdio.h>
28*3c7ae9deSAndroid Build Coastguard Worker
29*3c7ae9deSAndroid Build Coastguard Worker #include "crt.h"
30*3c7ae9deSAndroid Build Coastguard Worker #include "java_class_ids.h"
31*3c7ae9deSAndroid Build Coastguard Worker #include "logging.h"
32*3c7ae9deSAndroid Build Coastguard Worker
33*3c7ae9deSAndroid Build Coastguard Worker /* 0 = off, 1 = bytes, 2 = stack traces, see aws_mem_trace_level */
34*3c7ae9deSAndroid Build Coastguard Worker int g_memory_tracing = 0;
s_init_allocator(void)35*3c7ae9deSAndroid Build Coastguard Worker static struct aws_allocator *s_init_allocator(void) {
36*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing) {
37*3c7ae9deSAndroid Build Coastguard Worker struct aws_allocator *allocator = aws_default_allocator();
38*3c7ae9deSAndroid Build Coastguard Worker allocator = aws_mem_tracer_new(allocator, NULL, (enum aws_mem_trace_level)g_memory_tracing, 8);
39*3c7ae9deSAndroid Build Coastguard Worker return allocator;
40*3c7ae9deSAndroid Build Coastguard Worker }
41*3c7ae9deSAndroid Build Coastguard Worker return aws_default_allocator();
42*3c7ae9deSAndroid Build Coastguard Worker }
43*3c7ae9deSAndroid Build Coastguard Worker
44*3c7ae9deSAndroid Build Coastguard Worker static struct aws_allocator *s_allocator = NULL;
aws_jni_get_allocator(void)45*3c7ae9deSAndroid Build Coastguard Worker struct aws_allocator *aws_jni_get_allocator(void) {
46*3c7ae9deSAndroid Build Coastguard Worker if (AWS_UNLIKELY(s_allocator == NULL)) {
47*3c7ae9deSAndroid Build Coastguard Worker s_allocator = s_init_allocator();
48*3c7ae9deSAndroid Build Coastguard Worker }
49*3c7ae9deSAndroid Build Coastguard Worker return s_allocator;
50*3c7ae9deSAndroid Build Coastguard Worker }
51*3c7ae9deSAndroid Build Coastguard Worker
s_detach_jvm_from_thread(void * user_data)52*3c7ae9deSAndroid Build Coastguard Worker static void s_detach_jvm_from_thread(void *user_data) {
53*3c7ae9deSAndroid Build Coastguard Worker AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "s_detach_jvm_from_thread invoked");
54*3c7ae9deSAndroid Build Coastguard Worker JavaVM *jvm = user_data;
55*3c7ae9deSAndroid Build Coastguard Worker
56*3c7ae9deSAndroid Build Coastguard Worker /* we don't need this JNIEnv, but this is an easy way to verify the JVM is still valid to use */
57*3c7ae9deSAndroid Build Coastguard Worker /********** JNI ENV ACQUIRE **********/
58*3c7ae9deSAndroid Build Coastguard Worker JNIEnv *env = aws_jni_acquire_thread_env(jvm);
59*3c7ae9deSAndroid Build Coastguard Worker if (env != NULL) {
60*3c7ae9deSAndroid Build Coastguard Worker (*jvm)->DetachCurrentThread(jvm);
61*3c7ae9deSAndroid Build Coastguard Worker
62*3c7ae9deSAndroid Build Coastguard Worker aws_jni_release_thread_env(jvm, env);
63*3c7ae9deSAndroid Build Coastguard Worker /********** JNI ENV RELEASE **********/
64*3c7ae9deSAndroid Build Coastguard Worker }
65*3c7ae9deSAndroid Build Coastguard Worker }
66*3c7ae9deSAndroid Build Coastguard Worker
s_aws_jni_get_thread_env(JavaVM * jvm)67*3c7ae9deSAndroid Build Coastguard Worker static JNIEnv *s_aws_jni_get_thread_env(JavaVM *jvm) {
68*3c7ae9deSAndroid Build Coastguard Worker #ifdef ANDROID
69*3c7ae9deSAndroid Build Coastguard Worker JNIEnv *env = NULL;
70*3c7ae9deSAndroid Build Coastguard Worker #else
71*3c7ae9deSAndroid Build Coastguard Worker void *env = NULL;
72*3c7ae9deSAndroid Build Coastguard Worker #endif
73*3c7ae9deSAndroid Build Coastguard Worker if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6) == JNI_EDETACHED) {
74*3c7ae9deSAndroid Build Coastguard Worker AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "s_aws_jni_get_thread_env returned detached, attaching");
75*3c7ae9deSAndroid Build Coastguard Worker
76*3c7ae9deSAndroid Build Coastguard Worker struct aws_string *thread_name = NULL;
77*3c7ae9deSAndroid Build Coastguard Worker if (aws_thread_current_name(aws_jni_get_allocator(), &thread_name)) {
78*3c7ae9deSAndroid Build Coastguard Worker /* Retrieving thread name can fail for multitude of reasons and is
79*3c7ae9deSAndroid Build Coastguard Worker not fatal. Ignore the error and continue. */
80*3c7ae9deSAndroid Build Coastguard Worker AWS_LOGF_DEBUG(AWS_LS_COMMON_GENERAL, "Failed to retrieve name for the thread.");
81*3c7ae9deSAndroid Build Coastguard Worker }
82*3c7ae9deSAndroid Build Coastguard Worker
83*3c7ae9deSAndroid Build Coastguard Worker struct JavaVMAttachArgs attach_args = {
84*3c7ae9deSAndroid Build Coastguard Worker .version = JNI_VERSION_1_6,
85*3c7ae9deSAndroid Build Coastguard Worker .name = NULL,
86*3c7ae9deSAndroid Build Coastguard Worker .group = NULL,
87*3c7ae9deSAndroid Build Coastguard Worker };
88*3c7ae9deSAndroid Build Coastguard Worker
89*3c7ae9deSAndroid Build Coastguard Worker if (thread_name != NULL) {
90*3c7ae9deSAndroid Build Coastguard Worker attach_args.name = (char *)aws_string_c_str(thread_name);
91*3c7ae9deSAndroid Build Coastguard Worker }
92*3c7ae9deSAndroid Build Coastguard Worker
93*3c7ae9deSAndroid Build Coastguard Worker #ifdef ANDROID
94*3c7ae9deSAndroid Build Coastguard Worker jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, &env, &attach_args);
95*3c7ae9deSAndroid Build Coastguard Worker #else
96*3c7ae9deSAndroid Build Coastguard Worker jint result = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, &attach_args);
97*3c7ae9deSAndroid Build Coastguard Worker #endif
98*3c7ae9deSAndroid Build Coastguard Worker
99*3c7ae9deSAndroid Build Coastguard Worker aws_string_destroy(thread_name);
100*3c7ae9deSAndroid Build Coastguard Worker
101*3c7ae9deSAndroid Build Coastguard Worker /* Ran out of memory, don't log in this case */
102*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(result != JNI_ENOMEM);
103*3c7ae9deSAndroid Build Coastguard Worker if (result != JNI_OK) {
104*3c7ae9deSAndroid Build Coastguard Worker fprintf(stderr, "Unrecoverable AttachCurrentThreadAsDaemon failed, JNI error code is %d\n", (int)result);
105*3c7ae9deSAndroid Build Coastguard Worker return NULL;
106*3c7ae9deSAndroid Build Coastguard Worker }
107*3c7ae9deSAndroid Build Coastguard Worker /* This should only happen in event loop threads, the JVM main thread attachment is
108*3c7ae9deSAndroid Build Coastguard Worker * managed by the JVM, so we only need to clean up event loop thread attachments */
109*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_thread_current_at_exit(s_detach_jvm_from_thread, (void *)jvm));
110*3c7ae9deSAndroid Build Coastguard Worker }
111*3c7ae9deSAndroid Build Coastguard Worker
112*3c7ae9deSAndroid Build Coastguard Worker return env;
113*3c7ae9deSAndroid Build Coastguard Worker }
114*3c7ae9deSAndroid Build Coastguard Worker
115*3c7ae9deSAndroid Build Coastguard Worker /*
116*3c7ae9deSAndroid Build Coastguard Worker A simple system to support unpredictable JVM shutdowns. In an ideal world, everyone would correctly use the
117*3c7ae9deSAndroid Build Coastguard Worker CrtResource ref counting and strict (aws_thread_managed_join_all) shutdown, but given the difficulty of using
118*3c7ae9deSAndroid Build Coastguard Worker them correctly, that's not a realistic expectation. So we need to come up with a way for JVM shutdowns to
119*3c7ae9deSAndroid Build Coastguard Worker not trigger crashes from native threads that try and call back to Java (where even the JavaVM pointer cached
120*3c7ae9deSAndroid Build Coastguard Worker on the binding object is now garbage) after the JVM has shutdown (but before the process has killed all of its
121*3c7ae9deSAndroid Build Coastguard Worker threads).
122*3c7ae9deSAndroid Build Coastguard Worker
123*3c7ae9deSAndroid Build Coastguard Worker Our system works as follows:
124*3c7ae9deSAndroid Build Coastguard Worker
125*3c7ae9deSAndroid Build Coastguard Worker We track the set of all active JVMs (since we don't correctly support multiple JVMs yet, this is always going to be
126*3c7ae9deSAndroid Build Coastguard Worker either one or zero for now). We protect this set with a read-write lock. Adding (CRT init) or removing (JVM
127*3c7ae9deSAndroid Build Coastguard Worker shutdown hook) a JVM from this set will take a write lock. Acquiring a JNIEnv from a tracked JVM, will take a read
128*3c7ae9deSAndroid Build Coastguard Worker lock, and releasing a JNIEnv will release the read lock.
129*3c7ae9deSAndroid Build Coastguard Worker
130*3c7ae9deSAndroid Build Coastguard Worker Acquiring a JNIEnv succeeds if the JVM in question is in our set, and fails otherwise. All users of a JNIEnv have
131*3c7ae9deSAndroid Build Coastguard Worker been hardened to check for null and just not call to Java in that case.
132*3c7ae9deSAndroid Build Coastguard Worker
133*3c7ae9deSAndroid Build Coastguard Worker Since we don't have RAII in C, bindings must be very careful to release once, and exactly once, every JNIEnv that
134*3c7ae9deSAndroid Build Coastguard Worker they acquire. An alternative approach would be to replace all of the JNIEnv usage with a new API that
135*3c7ae9deSAndroid Build Coastguard Worker takes the lock, calls a supplied callback (which does all the JNIEnv operations), and then releases the lock. This
136*3c7ae9deSAndroid Build Coastguard Worker approach was tried but was so disruptive refactor-wise that I deemed it too dangerous to try and push through. So
137*3c7ae9deSAndroid Build Coastguard Worker instead, we just have to be careful with acquire/release.
138*3c7ae9deSAndroid Build Coastguard Worker
139*3c7ae9deSAndroid Build Coastguard Worker In this way, the vast majority of usage is relatively contentionless; it's just a bunch of native threads taking
140*3c7ae9deSAndroid Build Coastguard Worker read locks on a shared rw lock. Only when the JVM shutdown hook calls into native is there read-write contention.
141*3c7ae9deSAndroid Build Coastguard Worker */
142*3c7ae9deSAndroid Build Coastguard Worker static struct aws_rw_lock s_jvm_table_lock = AWS_RW_LOCK_INIT;
143*3c7ae9deSAndroid Build Coastguard Worker static struct aws_hash_table *s_jvms = NULL;
144*3c7ae9deSAndroid Build Coastguard Worker
s_jvm_table_add_jvm_for_env(JNIEnv * env)145*3c7ae9deSAndroid Build Coastguard Worker static void s_jvm_table_add_jvm_for_env(JNIEnv *env) {
146*3c7ae9deSAndroid Build Coastguard Worker aws_rw_lock_wlock(&s_jvm_table_lock);
147*3c7ae9deSAndroid Build Coastguard Worker
148*3c7ae9deSAndroid Build Coastguard Worker if (s_jvms == NULL) {
149*3c7ae9deSAndroid Build Coastguard Worker /* use default allocator so that tracing allocator doesn't flag this as a leak during tests */
150*3c7ae9deSAndroid Build Coastguard Worker s_jvms = aws_mem_calloc(aws_default_allocator(), 1, sizeof(struct aws_hash_table));
151*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(
152*3c7ae9deSAndroid Build Coastguard Worker AWS_OP_SUCCESS ==
153*3c7ae9deSAndroid Build Coastguard Worker aws_hash_table_init(s_jvms, aws_default_allocator(), 1, aws_hash_ptr, aws_ptr_eq, NULL, NULL));
154*3c7ae9deSAndroid Build Coastguard Worker }
155*3c7ae9deSAndroid Build Coastguard Worker
156*3c7ae9deSAndroid Build Coastguard Worker JavaVM *jvm = NULL;
157*3c7ae9deSAndroid Build Coastguard Worker jint jvmresult = (*env)->GetJavaVM(env, &jvm);
158*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(jvmresult == 0 && jvm != NULL);
159*3c7ae9deSAndroid Build Coastguard Worker
160*3c7ae9deSAndroid Build Coastguard Worker int was_created = 0;
161*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_hash_table_put(s_jvms, jvm, NULL, &was_created));
162*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(was_created == 1);
163*3c7ae9deSAndroid Build Coastguard Worker
164*3c7ae9deSAndroid Build Coastguard Worker aws_rw_lock_wunlock(&s_jvm_table_lock);
165*3c7ae9deSAndroid Build Coastguard Worker }
166*3c7ae9deSAndroid Build Coastguard Worker
s_jvm_table_remove_jvm_for_env(JNIEnv * env)167*3c7ae9deSAndroid Build Coastguard Worker static void s_jvm_table_remove_jvm_for_env(JNIEnv *env) {
168*3c7ae9deSAndroid Build Coastguard Worker aws_rw_lock_wlock(&s_jvm_table_lock);
169*3c7ae9deSAndroid Build Coastguard Worker
170*3c7ae9deSAndroid Build Coastguard Worker if (s_jvms == NULL) {
171*3c7ae9deSAndroid Build Coastguard Worker goto done;
172*3c7ae9deSAndroid Build Coastguard Worker }
173*3c7ae9deSAndroid Build Coastguard Worker
174*3c7ae9deSAndroid Build Coastguard Worker JavaVM *jvm = NULL;
175*3c7ae9deSAndroid Build Coastguard Worker jint jvmresult = (*env)->GetJavaVM(env, &jvm);
176*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(jvmresult == 0 && jvm != NULL);
177*3c7ae9deSAndroid Build Coastguard Worker
178*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(AWS_OP_SUCCESS == aws_hash_table_remove(s_jvms, jvm, NULL, NULL));
179*3c7ae9deSAndroid Build Coastguard Worker
180*3c7ae9deSAndroid Build Coastguard Worker if (aws_hash_table_get_entry_count(s_jvms) == 0) {
181*3c7ae9deSAndroid Build Coastguard Worker aws_hash_table_clean_up(s_jvms);
182*3c7ae9deSAndroid Build Coastguard Worker aws_mem_release(aws_default_allocator(), s_jvms);
183*3c7ae9deSAndroid Build Coastguard Worker s_jvms = NULL;
184*3c7ae9deSAndroid Build Coastguard Worker }
185*3c7ae9deSAndroid Build Coastguard Worker
186*3c7ae9deSAndroid Build Coastguard Worker done:
187*3c7ae9deSAndroid Build Coastguard Worker
188*3c7ae9deSAndroid Build Coastguard Worker aws_rw_lock_wunlock(&s_jvm_table_lock);
189*3c7ae9deSAndroid Build Coastguard Worker }
190*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_acquire_thread_env(JavaVM * jvm)191*3c7ae9deSAndroid Build Coastguard Worker JNIEnv *aws_jni_acquire_thread_env(JavaVM *jvm) {
192*3c7ae9deSAndroid Build Coastguard Worker /*
193*3c7ae9deSAndroid Build Coastguard Worker * We use try-lock here in order to avoid the re-entrant deadlock case that could happen if we have a read
194*3c7ae9deSAndroid Build Coastguard Worker * lock already, the JVM shutdown hooks causes another thread to block on taking the write lock, and then
195*3c7ae9deSAndroid Build Coastguard Worker * we try to reacquire the read-lock recursively due to some synchronous code path. That case can deadlock
196*3c7ae9deSAndroid Build Coastguard Worker * but since the JVM is going away, it's safe to just fail completely from here on out.
197*3c7ae9deSAndroid Build Coastguard Worker */
198*3c7ae9deSAndroid Build Coastguard Worker if (aws_rw_lock_try_rlock(&s_jvm_table_lock)) {
199*3c7ae9deSAndroid Build Coastguard Worker if (aws_last_error() != AWS_ERROR_UNSUPPORTED_OPERATION) {
200*3c7ae9deSAndroid Build Coastguard Worker aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
201*3c7ae9deSAndroid Build Coastguard Worker }
202*3c7ae9deSAndroid Build Coastguard Worker return NULL;
203*3c7ae9deSAndroid Build Coastguard Worker }
204*3c7ae9deSAndroid Build Coastguard Worker
205*3c7ae9deSAndroid Build Coastguard Worker if (s_jvms == NULL) {
206*3c7ae9deSAndroid Build Coastguard Worker aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
207*3c7ae9deSAndroid Build Coastguard Worker goto error;
208*3c7ae9deSAndroid Build Coastguard Worker }
209*3c7ae9deSAndroid Build Coastguard Worker
210*3c7ae9deSAndroid Build Coastguard Worker struct aws_hash_element *element = NULL;
211*3c7ae9deSAndroid Build Coastguard Worker int find_result = aws_hash_table_find(s_jvms, jvm, &element);
212*3c7ae9deSAndroid Build Coastguard Worker if (find_result != AWS_OP_SUCCESS || element == NULL) {
213*3c7ae9deSAndroid Build Coastguard Worker aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
214*3c7ae9deSAndroid Build Coastguard Worker goto error;
215*3c7ae9deSAndroid Build Coastguard Worker }
216*3c7ae9deSAndroid Build Coastguard Worker
217*3c7ae9deSAndroid Build Coastguard Worker JNIEnv *env = s_aws_jni_get_thread_env(jvm);
218*3c7ae9deSAndroid Build Coastguard Worker if (env == NULL) {
219*3c7ae9deSAndroid Build Coastguard Worker aws_raise_error(AWS_ERROR_JAVA_CRT_JVM_DESTROYED);
220*3c7ae9deSAndroid Build Coastguard Worker goto error;
221*3c7ae9deSAndroid Build Coastguard Worker }
222*3c7ae9deSAndroid Build Coastguard Worker
223*3c7ae9deSAndroid Build Coastguard Worker return env;
224*3c7ae9deSAndroid Build Coastguard Worker
225*3c7ae9deSAndroid Build Coastguard Worker error:
226*3c7ae9deSAndroid Build Coastguard Worker
227*3c7ae9deSAndroid Build Coastguard Worker aws_rw_lock_runlock(&s_jvm_table_lock);
228*3c7ae9deSAndroid Build Coastguard Worker
229*3c7ae9deSAndroid Build Coastguard Worker return NULL;
230*3c7ae9deSAndroid Build Coastguard Worker }
231*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_release_thread_env(JavaVM * jvm,JNIEnv * env)232*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_release_thread_env(JavaVM *jvm, JNIEnv *env) {
233*3c7ae9deSAndroid Build Coastguard Worker (void)jvm;
234*3c7ae9deSAndroid Build Coastguard Worker (void)env;
235*3c7ae9deSAndroid Build Coastguard Worker
236*3c7ae9deSAndroid Build Coastguard Worker if (env != NULL) {
237*3c7ae9deSAndroid Build Coastguard Worker aws_rw_lock_runlock(&s_jvm_table_lock);
238*3c7ae9deSAndroid Build Coastguard Worker }
239*3c7ae9deSAndroid Build Coastguard Worker }
240*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_throw_runtime_exception(JNIEnv * env,const char * msg,...)241*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_throw_runtime_exception(JNIEnv *env, const char *msg, ...) {
242*3c7ae9deSAndroid Build Coastguard Worker va_list args;
243*3c7ae9deSAndroid Build Coastguard Worker va_start(args, msg);
244*3c7ae9deSAndroid Build Coastguard Worker char buf[1024];
245*3c7ae9deSAndroid Build Coastguard Worker vsnprintf(buf, sizeof(buf), msg, args);
246*3c7ae9deSAndroid Build Coastguard Worker va_end(args);
247*3c7ae9deSAndroid Build Coastguard Worker
248*3c7ae9deSAndroid Build Coastguard Worker int error = aws_last_error();
249*3c7ae9deSAndroid Build Coastguard Worker char exception[1280];
250*3c7ae9deSAndroid Build Coastguard Worker snprintf(
251*3c7ae9deSAndroid Build Coastguard Worker exception,
252*3c7ae9deSAndroid Build Coastguard Worker sizeof(exception),
253*3c7ae9deSAndroid Build Coastguard Worker "%s (aws_last_error: %s(%d), %s)",
254*3c7ae9deSAndroid Build Coastguard Worker buf,
255*3c7ae9deSAndroid Build Coastguard Worker aws_error_name(error),
256*3c7ae9deSAndroid Build Coastguard Worker error,
257*3c7ae9deSAndroid Build Coastguard Worker aws_error_str(error));
258*3c7ae9deSAndroid Build Coastguard Worker jclass runtime_exception = crt_runtime_exception_properties.crt_runtime_exception_class;
259*3c7ae9deSAndroid Build Coastguard Worker (*env)->ThrowNew(env, runtime_exception, exception);
260*3c7ae9deSAndroid Build Coastguard Worker }
261*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_throw_null_pointer_exception(JNIEnv * env,const char * msg,...)262*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_throw_null_pointer_exception(JNIEnv *env, const char *msg, ...) {
263*3c7ae9deSAndroid Build Coastguard Worker va_list args;
264*3c7ae9deSAndroid Build Coastguard Worker va_start(args, msg);
265*3c7ae9deSAndroid Build Coastguard Worker char buf[1024];
266*3c7ae9deSAndroid Build Coastguard Worker vsnprintf(buf, sizeof(buf), msg, args);
267*3c7ae9deSAndroid Build Coastguard Worker va_end(args);
268*3c7ae9deSAndroid Build Coastguard Worker (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/NullPointerException"), buf);
269*3c7ae9deSAndroid Build Coastguard Worker }
270*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_throw_illegal_argument_exception(JNIEnv * env,const char * msg,...)271*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_throw_illegal_argument_exception(JNIEnv *env, const char *msg, ...) {
272*3c7ae9deSAndroid Build Coastguard Worker va_list args;
273*3c7ae9deSAndroid Build Coastguard Worker va_start(args, msg);
274*3c7ae9deSAndroid Build Coastguard Worker char buf[1024];
275*3c7ae9deSAndroid Build Coastguard Worker vsnprintf(buf, sizeof(buf), msg, args);
276*3c7ae9deSAndroid Build Coastguard Worker va_end(args);
277*3c7ae9deSAndroid Build Coastguard Worker (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"), buf);
278*3c7ae9deSAndroid Build Coastguard Worker }
279*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_check_and_clear_exception(JNIEnv * env)280*3c7ae9deSAndroid Build Coastguard Worker bool aws_jni_check_and_clear_exception(JNIEnv *env) {
281*3c7ae9deSAndroid Build Coastguard Worker bool exception_pending = (*env)->ExceptionCheck(env);
282*3c7ae9deSAndroid Build Coastguard Worker if (exception_pending) {
283*3c7ae9deSAndroid Build Coastguard Worker (*env)->ExceptionDescribe(env);
284*3c7ae9deSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
285*3c7ae9deSAndroid Build Coastguard Worker }
286*3c7ae9deSAndroid Build Coastguard Worker return exception_pending;
287*3c7ae9deSAndroid Build Coastguard Worker }
288*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_get_and_clear_exception(JNIEnv * env,jthrowable * out)289*3c7ae9deSAndroid Build Coastguard Worker bool aws_jni_get_and_clear_exception(JNIEnv *env, jthrowable *out) {
290*3c7ae9deSAndroid Build Coastguard Worker bool exception_pending = (*env)->ExceptionCheck(env);
291*3c7ae9deSAndroid Build Coastguard Worker if (exception_pending) {
292*3c7ae9deSAndroid Build Coastguard Worker (*env)->DeleteGlobalRef(env, *out);
293*3c7ae9deSAndroid Build Coastguard Worker *out = (jthrowable)(*env)->NewGlobalRef(env, (*env)->ExceptionOccurred(env));
294*3c7ae9deSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
295*3c7ae9deSAndroid Build Coastguard Worker }
296*3c7ae9deSAndroid Build Coastguard Worker return exception_pending;
297*3c7ae9deSAndroid Build Coastguard Worker }
298*3c7ae9deSAndroid Build Coastguard Worker
aws_size_t_from_java(JNIEnv * env,size_t * out_size,jlong java_long,const char * errmsg_prefix)299*3c7ae9deSAndroid Build Coastguard Worker int aws_size_t_from_java(JNIEnv *env, size_t *out_size, jlong java_long, const char *errmsg_prefix) {
300*3c7ae9deSAndroid Build Coastguard Worker if (java_long < 0) {
301*3c7ae9deSAndroid Build Coastguard Worker aws_jni_throw_illegal_argument_exception(env, "%s cannot be negative", errmsg_prefix);
302*3c7ae9deSAndroid Build Coastguard Worker goto error;
303*3c7ae9deSAndroid Build Coastguard Worker }
304*3c7ae9deSAndroid Build Coastguard Worker
305*3c7ae9deSAndroid Build Coastguard Worker if ((uint64_t)java_long > (uint64_t)SIZE_MAX) {
306*3c7ae9deSAndroid Build Coastguard Worker aws_jni_throw_illegal_argument_exception(
307*3c7ae9deSAndroid Build Coastguard Worker env, "%s cannot exceed %zu when running 32bit", errmsg_prefix, SIZE_MAX);
308*3c7ae9deSAndroid Build Coastguard Worker goto error;
309*3c7ae9deSAndroid Build Coastguard Worker }
310*3c7ae9deSAndroid Build Coastguard Worker
311*3c7ae9deSAndroid Build Coastguard Worker *out_size = (size_t)java_long;
312*3c7ae9deSAndroid Build Coastguard Worker return AWS_OP_SUCCESS;
313*3c7ae9deSAndroid Build Coastguard Worker error:
314*3c7ae9deSAndroid Build Coastguard Worker *out_size = 0;
315*3c7ae9deSAndroid Build Coastguard Worker return AWS_OP_ERR;
316*3c7ae9deSAndroid Build Coastguard Worker }
317*3c7ae9deSAndroid Build Coastguard Worker
aws_java_byte_array_new(JNIEnv * env,size_t size)318*3c7ae9deSAndroid Build Coastguard Worker jbyteArray aws_java_byte_array_new(JNIEnv *env, size_t size) {
319*3c7ae9deSAndroid Build Coastguard Worker jbyteArray jArray = (*env)->NewByteArray(env, (jsize)size);
320*3c7ae9deSAndroid Build Coastguard Worker return jArray;
321*3c7ae9deSAndroid Build Coastguard Worker }
322*3c7ae9deSAndroid Build Coastguard Worker
aws_copy_native_array_to_java_byte_array(JNIEnv * env,jbyteArray dst,uint8_t * src,size_t amount)323*3c7ae9deSAndroid Build Coastguard Worker bool aws_copy_native_array_to_java_byte_array(JNIEnv *env, jbyteArray dst, uint8_t *src, size_t amount) {
324*3c7ae9deSAndroid Build Coastguard Worker (*env)->SetByteArrayRegion(env, dst, 0, (jsize)amount, (jbyte *)src);
325*3c7ae9deSAndroid Build Coastguard Worker return aws_jni_check_and_clear_exception(env);
326*3c7ae9deSAndroid Build Coastguard Worker }
327*3c7ae9deSAndroid Build Coastguard Worker
328*3c7ae9deSAndroid Build Coastguard Worker /**
329*3c7ae9deSAndroid Build Coastguard Worker * Converts a Native aws_byte_cursor to a Java byte[]
330*3c7ae9deSAndroid Build Coastguard Worker */
aws_jni_byte_array_from_cursor(JNIEnv * env,const struct aws_byte_cursor * native_data)331*3c7ae9deSAndroid Build Coastguard Worker jbyteArray aws_jni_byte_array_from_cursor(JNIEnv *env, const struct aws_byte_cursor *native_data) {
332*3c7ae9deSAndroid Build Coastguard Worker jbyteArray jArray = aws_java_byte_array_new(env, native_data->len);
333*3c7ae9deSAndroid Build Coastguard Worker if (jArray) {
334*3c7ae9deSAndroid Build Coastguard Worker if (!aws_copy_native_array_to_java_byte_array(env, jArray, native_data->ptr, native_data->len)) {
335*3c7ae9deSAndroid Build Coastguard Worker return jArray;
336*3c7ae9deSAndroid Build Coastguard Worker }
337*3c7ae9deSAndroid Build Coastguard Worker }
338*3c7ae9deSAndroid Build Coastguard Worker return NULL;
339*3c7ae9deSAndroid Build Coastguard Worker }
340*3c7ae9deSAndroid Build Coastguard Worker
341*3c7ae9deSAndroid Build Coastguard Worker /**
342*3c7ae9deSAndroid Build Coastguard Worker * Get the Buffer Position (the next element to read/write)
343*3c7ae9deSAndroid Build Coastguard Worker */
aws_jni_byte_buffer_get_position(JNIEnv * env,jobject java_byte_buffer)344*3c7ae9deSAndroid Build Coastguard Worker int aws_jni_byte_buffer_get_position(JNIEnv *env, jobject java_byte_buffer) {
345*3c7ae9deSAndroid Build Coastguard Worker jint position = (*env)->CallIntMethod(env, java_byte_buffer, byte_buffer_properties.get_position);
346*3c7ae9deSAndroid Build Coastguard Worker return (aws_jni_check_and_clear_exception(env)) ? -1 : (int)position;
347*3c7ae9deSAndroid Build Coastguard Worker }
348*3c7ae9deSAndroid Build Coastguard Worker
349*3c7ae9deSAndroid Build Coastguard Worker /**
350*3c7ae9deSAndroid Build Coastguard Worker * Set the Buffer Position (the next element to read/write)
351*3c7ae9deSAndroid Build Coastguard Worker */
aws_jni_byte_buffer_set_position(JNIEnv * env,jobject jByteBuf,jint position)352*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_byte_buffer_set_position(JNIEnv *env, jobject jByteBuf, jint position) {
353*3c7ae9deSAndroid Build Coastguard Worker jobject val = (*env)->CallObjectMethod(env, jByteBuf, byte_buffer_properties.set_position, position);
354*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
355*3c7ae9deSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, val);
356*3c7ae9deSAndroid Build Coastguard Worker }
357*3c7ae9deSAndroid Build Coastguard Worker
358*3c7ae9deSAndroid Build Coastguard Worker /**
359*3c7ae9deSAndroid Build Coastguard Worker * Set the Buffer Limit (the max allowed element to read/write)
360*3c7ae9deSAndroid Build Coastguard Worker */
aws_jni_byte_buffer_set_limit(JNIEnv * env,jobject jByteBuf,jint limit)361*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_byte_buffer_set_limit(JNIEnv *env, jobject jByteBuf, jint limit) {
362*3c7ae9deSAndroid Build Coastguard Worker jobject val = (*env)->CallObjectMethod(env, jByteBuf, byte_buffer_properties.set_limit, limit);
363*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(!aws_jni_check_and_clear_exception(env));
364*3c7ae9deSAndroid Build Coastguard Worker (*env)->DeleteLocalRef(env, val);
365*3c7ae9deSAndroid Build Coastguard Worker }
366*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_direct_byte_buffer_from_raw_ptr(JNIEnv * env,const void * dst,size_t capacity)367*3c7ae9deSAndroid Build Coastguard Worker jobject aws_jni_direct_byte_buffer_from_raw_ptr(JNIEnv *env, const void *dst, size_t capacity) {
368*3c7ae9deSAndroid Build Coastguard Worker
369*3c7ae9deSAndroid Build Coastguard Worker jobject jByteBuf = (*env)->NewDirectByteBuffer(env, (void *)dst, (jlong)capacity);
370*3c7ae9deSAndroid Build Coastguard Worker if (jByteBuf) {
371*3c7ae9deSAndroid Build Coastguard Worker aws_jni_byte_buffer_set_limit(env, jByteBuf, (jint)capacity);
372*3c7ae9deSAndroid Build Coastguard Worker aws_jni_byte_buffer_set_position(env, jByteBuf, 0);
373*3c7ae9deSAndroid Build Coastguard Worker }
374*3c7ae9deSAndroid Build Coastguard Worker
375*3c7ae9deSAndroid Build Coastguard Worker return jByteBuf;
376*3c7ae9deSAndroid Build Coastguard Worker }
377*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_byte_cursor_from_jstring_acquire(JNIEnv * env,jstring str)378*3c7ae9deSAndroid Build Coastguard Worker struct aws_byte_cursor aws_jni_byte_cursor_from_jstring_acquire(JNIEnv *env, jstring str) {
379*3c7ae9deSAndroid Build Coastguard Worker if (str == NULL) {
380*3c7ae9deSAndroid Build Coastguard Worker aws_jni_throw_null_pointer_exception(env, "string is null");
381*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(NULL, 0);
382*3c7ae9deSAndroid Build Coastguard Worker }
383*3c7ae9deSAndroid Build Coastguard Worker
384*3c7ae9deSAndroid Build Coastguard Worker const char *bytes = (*env)->GetStringUTFChars(env, str, NULL);
385*3c7ae9deSAndroid Build Coastguard Worker if (bytes == NULL) {
386*3c7ae9deSAndroid Build Coastguard Worker /* GetStringUTFChars() has thrown exception */
387*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(NULL, 0);
388*3c7ae9deSAndroid Build Coastguard Worker }
389*3c7ae9deSAndroid Build Coastguard Worker
390*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(bytes, (size_t)(*env)->GetStringUTFLength(env, str));
391*3c7ae9deSAndroid Build Coastguard Worker }
392*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_byte_cursor_from_jstring_release(JNIEnv * env,jstring str,struct aws_byte_cursor cur)393*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_byte_cursor_from_jstring_release(JNIEnv *env, jstring str, struct aws_byte_cursor cur) {
394*3c7ae9deSAndroid Build Coastguard Worker if (cur.ptr != NULL) {
395*3c7ae9deSAndroid Build Coastguard Worker (*env)->ReleaseStringUTFChars(env, str, (const char *)cur.ptr);
396*3c7ae9deSAndroid Build Coastguard Worker }
397*3c7ae9deSAndroid Build Coastguard Worker }
398*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_byte_cursor_from_jbyteArray_acquire(JNIEnv * env,jbyteArray array)399*3c7ae9deSAndroid Build Coastguard Worker struct aws_byte_cursor aws_jni_byte_cursor_from_jbyteArray_acquire(JNIEnv *env, jbyteArray array) {
400*3c7ae9deSAndroid Build Coastguard Worker if (array == NULL) {
401*3c7ae9deSAndroid Build Coastguard Worker aws_jni_throw_null_pointer_exception(env, "byte[] is null");
402*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(NULL, 0);
403*3c7ae9deSAndroid Build Coastguard Worker }
404*3c7ae9deSAndroid Build Coastguard Worker
405*3c7ae9deSAndroid Build Coastguard Worker jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL);
406*3c7ae9deSAndroid Build Coastguard Worker if (bytes == NULL) {
407*3c7ae9deSAndroid Build Coastguard Worker /* GetByteArrayElements() has thrown exception */
408*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(NULL, 0);
409*3c7ae9deSAndroid Build Coastguard Worker }
410*3c7ae9deSAndroid Build Coastguard Worker
411*3c7ae9deSAndroid Build Coastguard Worker size_t len = (*env)->GetArrayLength(env, array);
412*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(bytes, len);
413*3c7ae9deSAndroid Build Coastguard Worker }
414*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_byte_cursor_from_jbyteArray_release(JNIEnv * env,jbyteArray array,struct aws_byte_cursor cur)415*3c7ae9deSAndroid Build Coastguard Worker void aws_jni_byte_cursor_from_jbyteArray_release(JNIEnv *env, jbyteArray array, struct aws_byte_cursor cur) {
416*3c7ae9deSAndroid Build Coastguard Worker if (cur.ptr != NULL) {
417*3c7ae9deSAndroid Build Coastguard Worker (*env)->ReleaseByteArrayElements(env, array, (jbyte *)cur.ptr, JNI_ABORT);
418*3c7ae9deSAndroid Build Coastguard Worker }
419*3c7ae9deSAndroid Build Coastguard Worker }
420*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_byte_cursor_from_direct_byte_buffer(JNIEnv * env,jobject byte_buffer)421*3c7ae9deSAndroid Build Coastguard Worker struct aws_byte_cursor aws_jni_byte_cursor_from_direct_byte_buffer(JNIEnv *env, jobject byte_buffer) {
422*3c7ae9deSAndroid Build Coastguard Worker jlong payload_size = (*env)->GetDirectBufferCapacity(env, byte_buffer);
423*3c7ae9deSAndroid Build Coastguard Worker if (payload_size == -1) {
424*3c7ae9deSAndroid Build Coastguard Worker aws_jni_throw_runtime_exception(
425*3c7ae9deSAndroid Build Coastguard Worker env, "MqttClientConnection.mqtt_publish: Unable to get capacity of payload ByteBuffer");
426*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(NULL, 0);
427*3c7ae9deSAndroid Build Coastguard Worker }
428*3c7ae9deSAndroid Build Coastguard Worker jbyte *payload_data = (*env)->GetDirectBufferAddress(env, byte_buffer);
429*3c7ae9deSAndroid Build Coastguard Worker if (!payload_data) {
430*3c7ae9deSAndroid Build Coastguard Worker aws_jni_throw_runtime_exception(
431*3c7ae9deSAndroid Build Coastguard Worker env, "MqttClientConnection.mqtt_publish: Unable to get buffer from payload ByteBuffer");
432*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array(NULL, 0);
433*3c7ae9deSAndroid Build Coastguard Worker }
434*3c7ae9deSAndroid Build Coastguard Worker return aws_byte_cursor_from_array((const uint8_t *)payload_data, (size_t)payload_size);
435*3c7ae9deSAndroid Build Coastguard Worker }
436*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_new_string_from_jstring(JNIEnv * env,jstring str)437*3c7ae9deSAndroid Build Coastguard Worker struct aws_string *aws_jni_new_string_from_jstring(JNIEnv *env, jstring str) {
438*3c7ae9deSAndroid Build Coastguard Worker struct aws_allocator *allocator = aws_jni_get_allocator();
439*3c7ae9deSAndroid Build Coastguard Worker const char *str_chars = (*env)->GetStringUTFChars(env, str, NULL);
440*3c7ae9deSAndroid Build Coastguard Worker if (!str_chars) {
441*3c7ae9deSAndroid Build Coastguard Worker aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
442*3c7ae9deSAndroid Build Coastguard Worker return NULL;
443*3c7ae9deSAndroid Build Coastguard Worker }
444*3c7ae9deSAndroid Build Coastguard Worker struct aws_string *result = aws_string_new_from_c_str(allocator, str_chars);
445*3c7ae9deSAndroid Build Coastguard Worker (*env)->ReleaseStringUTFChars(env, str, str_chars);
446*3c7ae9deSAndroid Build Coastguard Worker return result;
447*3c7ae9deSAndroid Build Coastguard Worker }
448*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_new_crt_exception_from_error_code(JNIEnv * env,int error_code)449*3c7ae9deSAndroid Build Coastguard Worker jobject aws_jni_new_crt_exception_from_error_code(JNIEnv *env, int error_code) {
450*3c7ae9deSAndroid Build Coastguard Worker jint jni_error_code = error_code;
451*3c7ae9deSAndroid Build Coastguard Worker
452*3c7ae9deSAndroid Build Coastguard Worker jobject crt_exception = (*env)->NewObject(
453*3c7ae9deSAndroid Build Coastguard Worker env,
454*3c7ae9deSAndroid Build Coastguard Worker crt_runtime_exception_properties.crt_runtime_exception_class,
455*3c7ae9deSAndroid Build Coastguard Worker crt_runtime_exception_properties.constructor_method_id,
456*3c7ae9deSAndroid Build Coastguard Worker jni_error_code);
457*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(crt_exception);
458*3c7ae9deSAndroid Build Coastguard Worker return crt_exception;
459*3c7ae9deSAndroid Build Coastguard Worker }
460*3c7ae9deSAndroid Build Coastguard Worker
461*3c7ae9deSAndroid Build Coastguard Worker #define AWS_DEFINE_ERROR_INFO_CRT(CODE, STR) AWS_DEFINE_ERROR_INFO(CODE, STR, "aws-crt-java")
462*3c7ae9deSAndroid Build Coastguard Worker
463*3c7ae9deSAndroid Build Coastguard Worker /* clang-format off */
464*3c7ae9deSAndroid Build Coastguard Worker static struct aws_error_info s_crt_errors[] = {
465*3c7ae9deSAndroid Build Coastguard Worker AWS_DEFINE_ERROR_INFO_CRT(
466*3c7ae9deSAndroid Build Coastguard Worker AWS_ERROR_JAVA_CRT_JVM_DESTROYED,
467*3c7ae9deSAndroid Build Coastguard Worker "Attempt to use a JVM that has already been destroyed"),
468*3c7ae9deSAndroid Build Coastguard Worker AWS_DEFINE_ERROR_INFO_CRT(
469*3c7ae9deSAndroid Build Coastguard Worker AWS_ERROR_JAVA_CRT_JVM_OUT_OF_MEMORY,
470*3c7ae9deSAndroid Build Coastguard Worker "OutOfMemoryError has been raised from JVM."),
471*3c7ae9deSAndroid Build Coastguard Worker };
472*3c7ae9deSAndroid Build Coastguard Worker /* clang-format on */
473*3c7ae9deSAndroid Build Coastguard Worker
474*3c7ae9deSAndroid Build Coastguard Worker static struct aws_error_info_list s_crt_error_list = {
475*3c7ae9deSAndroid Build Coastguard Worker .error_list = s_crt_errors,
476*3c7ae9deSAndroid Build Coastguard Worker .count = sizeof(s_crt_errors) / sizeof(struct aws_error_info),
477*3c7ae9deSAndroid Build Coastguard Worker };
478*3c7ae9deSAndroid Build Coastguard Worker
479*3c7ae9deSAndroid Build Coastguard Worker static struct aws_log_subject_info s_crt_log_subject_infos[] = {
480*3c7ae9deSAndroid Build Coastguard Worker /* clang-format off */
481*3c7ae9deSAndroid Build Coastguard Worker DEFINE_LOG_SUBJECT_INFO(
482*3c7ae9deSAndroid Build Coastguard Worker AWS_LS_JAVA_CRT_GENERAL, "JavaCrtGeneral", "Subject for aws-crt-java logging that defies categorization"),
483*3c7ae9deSAndroid Build Coastguard Worker DEFINE_LOG_SUBJECT_INFO(
484*3c7ae9deSAndroid Build Coastguard Worker AWS_LS_JAVA_CRT_RESOURCE, "JavaCrtResource", "Subject for CrtResource"),
485*3c7ae9deSAndroid Build Coastguard Worker DEFINE_LOG_SUBJECT_INFO(
486*3c7ae9deSAndroid Build Coastguard Worker AWS_LS_JAVA_CRT_S3, "JavaCrtS3", "Subject for the layer binding aws-c-s3 to Java"),
487*3c7ae9deSAndroid Build Coastguard Worker DEFINE_LOG_SUBJECT_INFO(
488*3c7ae9deSAndroid Build Coastguard Worker AWS_LS_JAVA_ANDROID_KEYCHAIN, "android-keychain", "Subject for Android KeyChain"),
489*3c7ae9deSAndroid Build Coastguard Worker /* clang-format on */
490*3c7ae9deSAndroid Build Coastguard Worker };
491*3c7ae9deSAndroid Build Coastguard Worker
492*3c7ae9deSAndroid Build Coastguard Worker static struct aws_log_subject_info_list s_crt_log_subject_list = {
493*3c7ae9deSAndroid Build Coastguard Worker .subject_list = s_crt_log_subject_infos,
494*3c7ae9deSAndroid Build Coastguard Worker .count = AWS_ARRAY_SIZE(s_crt_log_subject_infos),
495*3c7ae9deSAndroid Build Coastguard Worker };
496*3c7ae9deSAndroid Build Coastguard Worker
s_jni_atexit_strict(void)497*3c7ae9deSAndroid Build Coastguard Worker static void s_jni_atexit_strict(void) {
498*3c7ae9deSAndroid Build Coastguard Worker
499*3c7ae9deSAndroid Build Coastguard Worker aws_unregister_log_subject_info_list(&s_crt_log_subject_list);
500*3c7ae9deSAndroid Build Coastguard Worker aws_unregister_error_info(&s_crt_error_list);
501*3c7ae9deSAndroid Build Coastguard Worker
502*3c7ae9deSAndroid Build Coastguard Worker aws_s3_library_clean_up();
503*3c7ae9deSAndroid Build Coastguard Worker aws_event_stream_library_clean_up();
504*3c7ae9deSAndroid Build Coastguard Worker aws_auth_library_clean_up();
505*3c7ae9deSAndroid Build Coastguard Worker aws_http_library_clean_up();
506*3c7ae9deSAndroid Build Coastguard Worker aws_mqtt_library_clean_up();
507*3c7ae9deSAndroid Build Coastguard Worker
508*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing) {
509*3c7ae9deSAndroid Build Coastguard Worker struct aws_allocator *tracer_allocator = aws_jni_get_allocator();
510*3c7ae9deSAndroid Build Coastguard Worker aws_mem_tracer_dump(tracer_allocator);
511*3c7ae9deSAndroid Build Coastguard Worker }
512*3c7ae9deSAndroid Build Coastguard Worker
513*3c7ae9deSAndroid Build Coastguard Worker aws_jni_cleanup_logging();
514*3c7ae9deSAndroid Build Coastguard Worker
515*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing) {
516*3c7ae9deSAndroid Build Coastguard Worker struct aws_allocator *tracer_allocator = aws_jni_get_allocator();
517*3c7ae9deSAndroid Build Coastguard Worker aws_mem_tracer_destroy(tracer_allocator);
518*3c7ae9deSAndroid Build Coastguard Worker }
519*3c7ae9deSAndroid Build Coastguard Worker
520*3c7ae9deSAndroid Build Coastguard Worker s_allocator = NULL;
521*3c7ae9deSAndroid Build Coastguard Worker }
522*3c7ae9deSAndroid Build Coastguard Worker
523*3c7ae9deSAndroid Build Coastguard Worker #define DEFAULT_MANAGED_SHUTDOWN_WAIT_IN_SECONDS 1
524*3c7ae9deSAndroid Build Coastguard Worker
s_jni_atexit_gentle(void)525*3c7ae9deSAndroid Build Coastguard Worker static void s_jni_atexit_gentle(void) {
526*3c7ae9deSAndroid Build Coastguard Worker
527*3c7ae9deSAndroid Build Coastguard Worker /* If not doing strict shutdown, wait only a short time before shutting down */
528*3c7ae9deSAndroid Build Coastguard Worker aws_thread_set_managed_join_timeout_ns(
529*3c7ae9deSAndroid Build Coastguard Worker aws_timestamp_convert(DEFAULT_MANAGED_SHUTDOWN_WAIT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL));
530*3c7ae9deSAndroid Build Coastguard Worker
531*3c7ae9deSAndroid Build Coastguard Worker if (aws_thread_join_all_managed() == AWS_OP_SUCCESS) {
532*3c7ae9deSAndroid Build Coastguard Worker /* a successful managed join means it should be safe to do a full, strict clean up */
533*3c7ae9deSAndroid Build Coastguard Worker s_jni_atexit_strict();
534*3c7ae9deSAndroid Build Coastguard Worker } else {
535*3c7ae9deSAndroid Build Coastguard Worker /*
536*3c7ae9deSAndroid Build Coastguard Worker * We didn't successfully join all our threads so it's not really safe to clean up the libraries.
537*3c7ae9deSAndroid Build Coastguard Worker * Just dump memory if applicable and exit.
538*3c7ae9deSAndroid Build Coastguard Worker */
539*3c7ae9deSAndroid Build Coastguard Worker AWS_LOGF_WARN(
540*3c7ae9deSAndroid Build Coastguard Worker AWS_LS_JAVA_CRT_GENERAL,
541*3c7ae9deSAndroid Build Coastguard Worker "Not all native threads were successfully joined during gentle shutdown. Memory may be leaked.");
542*3c7ae9deSAndroid Build Coastguard Worker
543*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing) {
544*3c7ae9deSAndroid Build Coastguard Worker AWS_LOGF_DEBUG(
545*3c7ae9deSAndroid Build Coastguard Worker AWS_LS_JAVA_CRT_GENERAL,
546*3c7ae9deSAndroid Build Coastguard Worker "At shutdown, %u bytes remaining",
547*3c7ae9deSAndroid Build Coastguard Worker (uint32_t)aws_mem_tracer_bytes(aws_jni_get_allocator()));
548*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing > 1) {
549*3c7ae9deSAndroid Build Coastguard Worker aws_mem_tracer_dump(aws_jni_get_allocator());
550*3c7ae9deSAndroid Build Coastguard Worker }
551*3c7ae9deSAndroid Build Coastguard Worker }
552*3c7ae9deSAndroid Build Coastguard Worker }
553*3c7ae9deSAndroid Build Coastguard Worker }
554*3c7ae9deSAndroid Build Coastguard Worker
555*3c7ae9deSAndroid Build Coastguard Worker #define KB_256 (256 * 1024)
556*3c7ae9deSAndroid Build Coastguard Worker
557*3c7ae9deSAndroid Build Coastguard Worker /* Called as the entry point, immediately after the shared lib is loaded the first time by JNI */
558*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsCrtInit(JNIEnv * env,jclass jni_crt_class,jint jni_memtrace,jboolean jni_debug_wait,jboolean jni_strict_shutdown)559*3c7ae9deSAndroid Build Coastguard Worker void JNICALL Java_software_amazon_awssdk_crt_CRT_awsCrtInit(
560*3c7ae9deSAndroid Build Coastguard Worker JNIEnv *env,
561*3c7ae9deSAndroid Build Coastguard Worker jclass jni_crt_class,
562*3c7ae9deSAndroid Build Coastguard Worker jint jni_memtrace,
563*3c7ae9deSAndroid Build Coastguard Worker jboolean jni_debug_wait,
564*3c7ae9deSAndroid Build Coastguard Worker jboolean jni_strict_shutdown) {
565*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_class;
566*3c7ae9deSAndroid Build Coastguard Worker
567*3c7ae9deSAndroid Build Coastguard Worker if (jni_debug_wait) {
568*3c7ae9deSAndroid Build Coastguard Worker bool done = false;
569*3c7ae9deSAndroid Build Coastguard Worker while (!done) {
570*3c7ae9deSAndroid Build Coastguard Worker ;
571*3c7ae9deSAndroid Build Coastguard Worker }
572*3c7ae9deSAndroid Build Coastguard Worker }
573*3c7ae9deSAndroid Build Coastguard Worker
574*3c7ae9deSAndroid Build Coastguard Worker g_memory_tracing = jni_memtrace;
575*3c7ae9deSAndroid Build Coastguard Worker
576*3c7ae9deSAndroid Build Coastguard Worker /*
577*3c7ae9deSAndroid Build Coastguard Worker * Increase the maximum channel message size in order to improve throughput on large payloads.
578*3c7ae9deSAndroid Build Coastguard Worker * Consider adding a system property override in the future.
579*3c7ae9deSAndroid Build Coastguard Worker */
580*3c7ae9deSAndroid Build Coastguard Worker g_aws_channel_max_fragment_size = KB_256;
581*3c7ae9deSAndroid Build Coastguard Worker
582*3c7ae9deSAndroid Build Coastguard Worker /* check to see if we have support for backtraces only if we need to */
583*3c7ae9deSAndroid Build Coastguard Worker void *stack[1];
584*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing > 1 && 0 == aws_backtrace(stack, 1)) {
585*3c7ae9deSAndroid Build Coastguard Worker g_memory_tracing = 1;
586*3c7ae9deSAndroid Build Coastguard Worker }
587*3c7ae9deSAndroid Build Coastguard Worker
588*3c7ae9deSAndroid Build Coastguard Worker /* NOT using aws_jni_get_allocator to avoid trace leak outside the test */
589*3c7ae9deSAndroid Build Coastguard Worker struct aws_allocator *allocator = aws_default_allocator();
590*3c7ae9deSAndroid Build Coastguard Worker aws_mqtt_library_init(allocator);
591*3c7ae9deSAndroid Build Coastguard Worker aws_http_library_init(allocator);
592*3c7ae9deSAndroid Build Coastguard Worker aws_auth_library_init(allocator);
593*3c7ae9deSAndroid Build Coastguard Worker aws_event_stream_library_init(allocator);
594*3c7ae9deSAndroid Build Coastguard Worker aws_s3_library_init(allocator);
595*3c7ae9deSAndroid Build Coastguard Worker
596*3c7ae9deSAndroid Build Coastguard Worker aws_register_error_info(&s_crt_error_list);
597*3c7ae9deSAndroid Build Coastguard Worker aws_register_log_subject_info_list(&s_crt_log_subject_list);
598*3c7ae9deSAndroid Build Coastguard Worker
599*3c7ae9deSAndroid Build Coastguard Worker s_jvm_table_add_jvm_for_env(env);
600*3c7ae9deSAndroid Build Coastguard Worker
601*3c7ae9deSAndroid Build Coastguard Worker if (jni_strict_shutdown) {
602*3c7ae9deSAndroid Build Coastguard Worker atexit(s_jni_atexit_strict);
603*3c7ae9deSAndroid Build Coastguard Worker } else {
604*3c7ae9deSAndroid Build Coastguard Worker atexit(s_jni_atexit_gentle);
605*3c7ae9deSAndroid Build Coastguard Worker }
606*3c7ae9deSAndroid Build Coastguard Worker }
607*3c7ae9deSAndroid Build Coastguard Worker
608*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_onJvmShutdown(JNIEnv * env,jclass jni_crt_class)609*3c7ae9deSAndroid Build Coastguard Worker void JNICALL Java_software_amazon_awssdk_crt_CRT_onJvmShutdown(JNIEnv *env, jclass jni_crt_class) {
610*3c7ae9deSAndroid Build Coastguard Worker
611*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_class;
612*3c7ae9deSAndroid Build Coastguard Worker
613*3c7ae9deSAndroid Build Coastguard Worker s_jvm_table_remove_jvm_for_env(env);
614*3c7ae9deSAndroid Build Coastguard Worker }
615*3c7ae9deSAndroid Build Coastguard Worker
616*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsLastError(JNIEnv * env,jclass jni_crt_class)617*3c7ae9deSAndroid Build Coastguard Worker jint JNICALL Java_software_amazon_awssdk_crt_CRT_awsLastError(JNIEnv *env, jclass jni_crt_class) {
618*3c7ae9deSAndroid Build Coastguard Worker (void)env;
619*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_class;
620*3c7ae9deSAndroid Build Coastguard Worker return aws_last_error();
621*3c7ae9deSAndroid Build Coastguard Worker }
622*3c7ae9deSAndroid Build Coastguard Worker
623*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsErrorString(JNIEnv * env,jclass jni_crt_class,jint error_code)624*3c7ae9deSAndroid Build Coastguard Worker jstring JNICALL Java_software_amazon_awssdk_crt_CRT_awsErrorString(JNIEnv *env, jclass jni_crt_class, jint error_code) {
625*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_class;
626*3c7ae9deSAndroid Build Coastguard Worker const char *error_msg = aws_error_str(error_code);
627*3c7ae9deSAndroid Build Coastguard Worker return (*env)->NewStringUTF(env, error_msg);
628*3c7ae9deSAndroid Build Coastguard Worker }
629*3c7ae9deSAndroid Build Coastguard Worker
630*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsErrorName(JNIEnv * env,jclass jni_crt_class,jint error_code)631*3c7ae9deSAndroid Build Coastguard Worker jstring JNICALL Java_software_amazon_awssdk_crt_CRT_awsErrorName(JNIEnv *env, jclass jni_crt_class, jint error_code) {
632*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_class;
633*3c7ae9deSAndroid Build Coastguard Worker const char *error_msg = aws_error_name(error_code);
634*3c7ae9deSAndroid Build Coastguard Worker return (*env)->NewStringUTF(env, error_msg);
635*3c7ae9deSAndroid Build Coastguard Worker }
636*3c7ae9deSAndroid Build Coastguard Worker
637*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_awsNativeMemory(JNIEnv * env,jclass jni_crt_class)638*3c7ae9deSAndroid Build Coastguard Worker jlong JNICALL Java_software_amazon_awssdk_crt_CRT_awsNativeMemory(JNIEnv *env, jclass jni_crt_class) {
639*3c7ae9deSAndroid Build Coastguard Worker (void)env;
640*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_class;
641*3c7ae9deSAndroid Build Coastguard Worker jlong allocated = 0;
642*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing) {
643*3c7ae9deSAndroid Build Coastguard Worker allocated = (jlong)aws_mem_tracer_bytes(aws_jni_get_allocator());
644*3c7ae9deSAndroid Build Coastguard Worker }
645*3c7ae9deSAndroid Build Coastguard Worker return allocated;
646*3c7ae9deSAndroid Build Coastguard Worker }
647*3c7ae9deSAndroid Build Coastguard Worker
648*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_dumpNativeMemory(JNIEnv * env,jclass jni_crt_class)649*3c7ae9deSAndroid Build Coastguard Worker void JNICALL Java_software_amazon_awssdk_crt_CRT_dumpNativeMemory(JNIEnv *env, jclass jni_crt_class) {
650*3c7ae9deSAndroid Build Coastguard Worker (void)env;
651*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_class;
652*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing > 1) {
653*3c7ae9deSAndroid Build Coastguard Worker aws_mem_tracer_dump(aws_jni_get_allocator());
654*3c7ae9deSAndroid Build Coastguard Worker }
655*3c7ae9deSAndroid Build Coastguard Worker }
656*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_string_from_cursor(JNIEnv * env,const struct aws_byte_cursor * native_data)657*3c7ae9deSAndroid Build Coastguard Worker jstring aws_jni_string_from_cursor(JNIEnv *env, const struct aws_byte_cursor *native_data) {
658*3c7ae9deSAndroid Build Coastguard Worker struct aws_string *string = aws_string_new_from_array(aws_jni_get_allocator(), native_data->ptr, native_data->len);
659*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(string != NULL);
660*3c7ae9deSAndroid Build Coastguard Worker
661*3c7ae9deSAndroid Build Coastguard Worker jstring java_string = (*env)->NewStringUTF(env, aws_string_c_str(string));
662*3c7ae9deSAndroid Build Coastguard Worker aws_string_destroy(string);
663*3c7ae9deSAndroid Build Coastguard Worker
664*3c7ae9deSAndroid Build Coastguard Worker return java_string;
665*3c7ae9deSAndroid Build Coastguard Worker }
666*3c7ae9deSAndroid Build Coastguard Worker
aws_jni_string_from_string(JNIEnv * env,const struct aws_string * string)667*3c7ae9deSAndroid Build Coastguard Worker jstring aws_jni_string_from_string(JNIEnv *env, const struct aws_string *string) {
668*3c7ae9deSAndroid Build Coastguard Worker AWS_FATAL_ASSERT(string != NULL);
669*3c7ae9deSAndroid Build Coastguard Worker
670*3c7ae9deSAndroid Build Coastguard Worker jstring java_string = (*env)->NewStringUTF(env, aws_string_c_str(string));
671*3c7ae9deSAndroid Build Coastguard Worker
672*3c7ae9deSAndroid Build Coastguard Worker return java_string;
673*3c7ae9deSAndroid Build Coastguard Worker }
674*3c7ae9deSAndroid Build Coastguard Worker
675*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CrtResource_waitForGlobalResourceDestruction(JNIEnv * env,jclass jni_crt_resource_class,jint timeout_in_seconds)676*3c7ae9deSAndroid Build Coastguard Worker void JNICALL Java_software_amazon_awssdk_crt_CrtResource_waitForGlobalResourceDestruction(
677*3c7ae9deSAndroid Build Coastguard Worker JNIEnv *env,
678*3c7ae9deSAndroid Build Coastguard Worker jclass jni_crt_resource_class,
679*3c7ae9deSAndroid Build Coastguard Worker jint timeout_in_seconds) {
680*3c7ae9deSAndroid Build Coastguard Worker (void)env;
681*3c7ae9deSAndroid Build Coastguard Worker (void)jni_crt_resource_class;
682*3c7ae9deSAndroid Build Coastguard Worker
683*3c7ae9deSAndroid Build Coastguard Worker aws_thread_set_managed_join_timeout_ns(
684*3c7ae9deSAndroid Build Coastguard Worker aws_timestamp_convert(timeout_in_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL));
685*3c7ae9deSAndroid Build Coastguard Worker aws_thread_join_all_managed();
686*3c7ae9deSAndroid Build Coastguard Worker
687*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing) {
688*3c7ae9deSAndroid Build Coastguard Worker AWS_LOGF_DEBUG(
689*3c7ae9deSAndroid Build Coastguard Worker AWS_LS_COMMON_GENERAL,
690*3c7ae9deSAndroid Build Coastguard Worker "At shutdown, %u bytes remaining",
691*3c7ae9deSAndroid Build Coastguard Worker (uint32_t)aws_mem_tracer_bytes(aws_jni_get_allocator()));
692*3c7ae9deSAndroid Build Coastguard Worker if (g_memory_tracing > 1) {
693*3c7ae9deSAndroid Build Coastguard Worker aws_mem_tracer_dump(aws_jni_get_allocator());
694*3c7ae9deSAndroid Build Coastguard Worker }
695*3c7ae9deSAndroid Build Coastguard Worker }
696*3c7ae9deSAndroid Build Coastguard Worker }
697*3c7ae9deSAndroid Build Coastguard Worker
698*3c7ae9deSAndroid Build Coastguard Worker JNIEXPORT
Java_software_amazon_awssdk_crt_CRT_nativeCheckJniExceptionContract(JNIEnv * env,jclass jni_crt_class,jboolean clear_exception)699*3c7ae9deSAndroid Build Coastguard Worker void JNICALL Java_software_amazon_awssdk_crt_CRT_nativeCheckJniExceptionContract(
700*3c7ae9deSAndroid Build Coastguard Worker JNIEnv *env,
701*3c7ae9deSAndroid Build Coastguard Worker jclass jni_crt_class,
702*3c7ae9deSAndroid Build Coastguard Worker jboolean clear_exception) {
703*3c7ae9deSAndroid Build Coastguard Worker
704*3c7ae9deSAndroid Build Coastguard Worker (*env)->CallStaticVoidMethod(env, jni_crt_class, crt_properties.test_jni_exception_method_id, true);
705*3c7ae9deSAndroid Build Coastguard Worker
706*3c7ae9deSAndroid Build Coastguard Worker if (clear_exception) {
707*3c7ae9deSAndroid Build Coastguard Worker (*env)->ExceptionClear(env);
708*3c7ae9deSAndroid Build Coastguard Worker (*env)->CallStaticVoidMethod(env, jni_crt_class, crt_properties.test_jni_exception_method_id, false);
709*3c7ae9deSAndroid Build Coastguard Worker } else {
710*3c7ae9deSAndroid Build Coastguard Worker (*env)->ExceptionCheck(env);
711*3c7ae9deSAndroid Build Coastguard Worker }
712*3c7ae9deSAndroid Build Coastguard Worker }
713