xref: /aosp_15_r20/external/aws-crt-java/src/native/crt.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
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