xref: /aosp_15_r20/external/libtextclassifier/native/utils/java/jni-cache.cc (revision 993b0882672172b81d12fad7a7ac0c3e5c824a12)
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "utils/java/jni-cache.h"
18 
19 #include "utils/base/logging.h"
20 #include "utils/base/status_macros.h"
21 #include "utils/java/jni-base.h"
22 #include "utils/java/jni-helper.h"
23 
24 namespace libtextclassifier3 {
25 
JniCache(JavaVM * jvm)26 JniCache::JniCache(JavaVM* jvm)
27     : jvm(jvm),
28       string_class(nullptr, jvm),
29       string_utf8(nullptr, jvm),
30       pattern_class(nullptr, jvm),
31       matcher_class(nullptr, jvm),
32       locale_class(nullptr, jvm),
33       locale_us(nullptr, jvm),
34       breakiterator_class(nullptr, jvm),
35       integer_class(nullptr, jvm),
36       calendar_class(nullptr, jvm),
37       timezone_class(nullptr, jvm)
38 #ifdef __ANDROID__
39       ,
40       context_class(nullptr, jvm),
41       uri_class(nullptr, jvm),
42       usermanager_class(nullptr, jvm),
43       bundle_class(nullptr, jvm),
44       resources_class(nullptr, jvm)
45 #endif
46 {
47 }
48 
49 // The macros below are intended to reduce the boilerplate in Create and avoid
50 // easily introduced copy/paste errors.
51 #define TC3_CHECK_JNI_PTR(PTR) TC3_CHECK((PTR) != nullptr)
52 #define TC3_CHECK_JNI_RESULT(RESULT) TC3_CHECK(RESULT)
53 
54 #define TC3_GET_CLASS_OR_RETURN_NULL(FIELD, NAME)                 \
55   {                                                               \
56     TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jclass> clazz,       \
57                               JniHelper::FindClass(env, NAME));   \
58     result->FIELD##_class = MakeGlobalRef(clazz.get(), env, jvm); \
59     if (result->FIELD##_class == nullptr) {                       \
60       TC3_LOG(ERROR) << "Error finding class: " << NAME;          \
61       return nullptr;                                             \
62     }                                                             \
63   }
64 
65 #define TC3_GET_OPTIONAL_CLASS(FIELD, NAME)                         \
66   {                                                                 \
67     StatusOr<ScopedLocalRef<jclass>> status_or_class =              \
68         JniHelper::FindClass(env, NAME);                            \
69     if (status_or_class.ok()) {                                     \
70       result->FIELD##_class = MakeGlobalRef(                        \
71           std::move(status_or_class).ValueOrDie().get(), env, jvm); \
72     }                                                               \
73   }
74 
75 #define TC3_GET_METHOD(CLASS, FIELD, NAME, SIGNATURE)                \
76   TC3_ASSIGN_OR_RETURN_NULL(                                         \
77       result->CLASS##_##FIELD,                                       \
78       JniHelper::GetMethodID(env, result->CLASS##_class.get(), NAME, \
79                              SIGNATURE));
80 
81 #define TC3_GET_OPTIONAL_METHOD(CLASS, FIELD, NAME, SIGNATURE) \
82   TC3_GET_OPTIONAL_METHOD_INTERNAL(CLASS, FIELD, NAME, SIGNATURE, GetMethodID)
83 
84 #define TC3_GET_OPTIONAL_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE) \
85   TC3_GET_OPTIONAL_METHOD_INTERNAL(CLASS, FIELD, NAME, SIGNATURE,     \
86                                    GetStaticMethodID)
87 
88 #define TC3_GET_OPTIONAL_METHOD_INTERNAL(CLASS, FIELD, NAME, SIGNATURE,   \
89                                          METHOD_NAME)                     \
90   if (result->CLASS##_class != nullptr) {                                 \
91     if (StatusOr<jmethodID> status_or_method_id = JniHelper::METHOD_NAME( \
92             env, result->CLASS##_class.get(), NAME, SIGNATURE);           \
93         status_or_method_id.ok()) {                                       \
94       result->CLASS##_##FIELD = status_or_method_id.ValueOrDie();         \
95     }                                                                     \
96   }
97 
98 #define TC3_GET_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE)               \
99   TC3_ASSIGN_OR_RETURN_NULL(                                               \
100       result->CLASS##_##FIELD,                                             \
101       JniHelper::GetStaticMethodID(env, result->CLASS##_class.get(), NAME, \
102                                    SIGNATURE));
103 
104 #define TC3_GET_STATIC_OBJECT_FIELD_OR_RETURN_NULL(CLASS, FIELD, NAME,      \
105                                                    SIGNATURE)               \
106   {                                                                         \
107     TC3_ASSIGN_OR_RETURN_NULL(                                              \
108         const jfieldID CLASS##_##FIELD##_field,                             \
109         JniHelper::GetStaticFieldID(env, result->CLASS##_class.get(), NAME, \
110                                     SIGNATURE));                            \
111     TC3_ASSIGN_OR_RETURN_NULL(                                              \
112         ScopedLocalRef<jobject> static_object,                              \
113         JniHelper::GetStaticObjectField(env, result->CLASS##_class.get(),   \
114                                         CLASS##_##FIELD##_field));          \
115     result->CLASS##_##FIELD = MakeGlobalRef(static_object.get(), env, jvm); \
116     if (result->CLASS##_##FIELD == nullptr) {                               \
117       TC3_LOG(ERROR) << "Error finding field: " << NAME;                    \
118       return nullptr;                                                       \
119     }                                                                       \
120   }
121 
122 #define TC3_GET_STATIC_INT_FIELD(CLASS, FIELD, NAME)                           \
123   TC3_ASSIGN_OR_RETURN_NULL(const jfieldID CLASS##_##FIELD##_field,            \
124                             JniHelper::GetStaticFieldID(                       \
125                                 env, result->CLASS##_class.get(), NAME, "I")); \
126   TC3_ASSIGN_OR_RETURN_NULL(                                                   \
127       result->CLASS##_##FIELD,                                                 \
128       JniHelper::GetStaticIntField(env, result->CLASS##_class.get(),           \
129                                    CLASS##_##FIELD##_field));
130 
Create(JNIEnv * env)131 std::unique_ptr<JniCache> JniCache::Create(JNIEnv* env) {
132   if (env == nullptr) {
133     return nullptr;
134   }
135   JavaVM* jvm = nullptr;
136   if (JNI_OK != env->GetJavaVM(&jvm) || jvm == nullptr) {
137     return nullptr;
138   }
139   std::unique_ptr<JniCache> result(new JniCache(jvm));
140 
141   // String
142   TC3_GET_CLASS_OR_RETURN_NULL(string, "java/lang/String");
143   TC3_GET_METHOD(string, init_bytes_charset, "<init>",
144                  "([BLjava/lang/String;)V");
145   TC3_GET_METHOD(string, code_point_count, "codePointCount", "(II)I");
146   TC3_GET_METHOD(string, length, "length", "()I");
147   TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> result_string,
148                             JniHelper::NewStringUTF(env, "UTF-8"));
149   result->string_utf8 = MakeGlobalRef(result_string.get(), env, jvm);
150   TC3_CHECK_JNI_PTR(result->string_utf8);
151 
152   // Pattern
153   TC3_GET_CLASS_OR_RETURN_NULL(pattern, "java/util/regex/Pattern");
154   TC3_GET_STATIC_METHOD(pattern, compile, "compile",
155                         "(Ljava/lang/String;)Ljava/util/regex/Pattern;");
156   TC3_GET_METHOD(pattern, matcher, "matcher",
157                  "(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;");
158 
159   // Matcher
160   TC3_GET_CLASS_OR_RETURN_NULL(matcher, "java/util/regex/Matcher");
161   TC3_GET_METHOD(matcher, matches, "matches", "()Z");
162   TC3_GET_METHOD(matcher, find, "find", "()Z");
163   TC3_GET_METHOD(matcher, reset, "reset", "()Ljava/util/regex/Matcher;");
164   TC3_GET_METHOD(matcher, start_idx, "start", "(I)I");
165   TC3_GET_METHOD(matcher, end_idx, "end", "(I)I");
166   TC3_GET_METHOD(matcher, group, "group", "()Ljava/lang/String;");
167   TC3_GET_METHOD(matcher, group_idx, "group", "(I)Ljava/lang/String;");
168 
169   // Locale
170   TC3_GET_CLASS_OR_RETURN_NULL(locale, "java/util/Locale");
171   TC3_GET_STATIC_OBJECT_FIELD_OR_RETURN_NULL(locale, us, "US",
172                                              "Ljava/util/Locale;");
173   TC3_GET_METHOD(locale, init_string, "<init>", "(Ljava/lang/String;)V");
174   TC3_GET_OPTIONAL_STATIC_METHOD(locale, for_language_tag, "forLanguageTag",
175                                  "(Ljava/lang/String;)Ljava/util/Locale;");
176 
177   // BreakIterator
178   TC3_GET_CLASS_OR_RETURN_NULL(breakiterator, "java/text/BreakIterator");
179   TC3_GET_STATIC_METHOD(breakiterator, getwordinstance, "getWordInstance",
180                         "(Ljava/util/Locale;)Ljava/text/BreakIterator;");
181   TC3_GET_METHOD(breakiterator, settext, "setText", "(Ljava/lang/String;)V");
182   TC3_GET_METHOD(breakiterator, next, "next", "()I");
183 
184   // Integer
185   TC3_GET_CLASS_OR_RETURN_NULL(integer, "java/lang/Integer");
186   TC3_GET_STATIC_METHOD(integer, parse_int, "parseInt",
187                         "(Ljava/lang/String;)I");
188 
189   // Calendar.
190   TC3_GET_CLASS_OR_RETURN_NULL(calendar, "java/util/Calendar");
191   TC3_GET_STATIC_METHOD(
192       calendar, get_instance, "getInstance",
193       "(Ljava/util/TimeZone;Ljava/util/Locale;)Ljava/util/Calendar;");
194   TC3_GET_METHOD(calendar, get_first_day_of_week, "getFirstDayOfWeek", "()I");
195   TC3_GET_METHOD(calendar, get_time_in_millis, "getTimeInMillis", "()J");
196   TC3_GET_METHOD(calendar, set_time_in_millis, "setTimeInMillis", "(J)V");
197   TC3_GET_METHOD(calendar, add, "add", "(II)V");
198   TC3_GET_METHOD(calendar, get, "get", "(I)I");
199   TC3_GET_METHOD(calendar, set, "set", "(II)V");
200   TC3_GET_STATIC_INT_FIELD(calendar, zone_offset, "ZONE_OFFSET");
201   TC3_GET_STATIC_INT_FIELD(calendar, dst_offset, "DST_OFFSET");
202   TC3_GET_STATIC_INT_FIELD(calendar, year, "YEAR");
203   TC3_GET_STATIC_INT_FIELD(calendar, month, "MONTH");
204   TC3_GET_STATIC_INT_FIELD(calendar, day_of_year, "DAY_OF_YEAR");
205   TC3_GET_STATIC_INT_FIELD(calendar, day_of_month, "DAY_OF_MONTH");
206   TC3_GET_STATIC_INT_FIELD(calendar, day_of_week, "DAY_OF_WEEK");
207   TC3_GET_STATIC_INT_FIELD(calendar, hour_of_day, "HOUR_OF_DAY");
208   TC3_GET_STATIC_INT_FIELD(calendar, minute, "MINUTE");
209   TC3_GET_STATIC_INT_FIELD(calendar, second, "SECOND");
210   TC3_GET_STATIC_INT_FIELD(calendar, millisecond, "MILLISECOND");
211   TC3_GET_STATIC_INT_FIELD(calendar, sunday, "SUNDAY");
212   TC3_GET_STATIC_INT_FIELD(calendar, monday, "MONDAY");
213   TC3_GET_STATIC_INT_FIELD(calendar, tuesday, "TUESDAY");
214   TC3_GET_STATIC_INT_FIELD(calendar, wednesday, "WEDNESDAY");
215   TC3_GET_STATIC_INT_FIELD(calendar, thursday, "THURSDAY");
216   TC3_GET_STATIC_INT_FIELD(calendar, friday, "FRIDAY");
217   TC3_GET_STATIC_INT_FIELD(calendar, saturday, "SATURDAY");
218 
219   // TimeZone.
220   TC3_GET_CLASS_OR_RETURN_NULL(timezone, "java/util/TimeZone");
221   TC3_GET_STATIC_METHOD(timezone, get_timezone, "getTimeZone",
222                         "(Ljava/lang/String;)Ljava/util/TimeZone;");
223 
224 #ifdef __ANDROID__
225   // Context.
226   TC3_GET_CLASS_OR_RETURN_NULL(context, "android/content/Context");
227   TC3_GET_METHOD(context, get_package_name, "getPackageName",
228                  "()Ljava/lang/String;");
229   TC3_GET_METHOD(context, get_system_service, "getSystemService",
230                  "(Ljava/lang/String;)Ljava/lang/Object;");
231 
232   // Uri.
233   TC3_GET_CLASS_OR_RETURN_NULL(uri, "android/net/Uri");
234   TC3_GET_STATIC_METHOD(uri, parse, "parse",
235                         "(Ljava/lang/String;)Landroid/net/Uri;");
236   TC3_GET_METHOD(uri, get_scheme, "getScheme", "()Ljava/lang/String;");
237   TC3_GET_METHOD(uri, get_host, "getHost", "()Ljava/lang/String;");
238   TC3_GET_STATIC_METHOD(uri, encode, "encode",
239                         "(Ljava/lang/String;)Ljava/lang/String;");
240 
241   // UserManager.
242   TC3_GET_OPTIONAL_CLASS(usermanager, "android/os/UserManager");
243   TC3_GET_OPTIONAL_METHOD(usermanager, get_user_restrictions,
244                           "getUserRestrictions", "()Landroid/os/Bundle;");
245 
246   // Bundle.
247   TC3_GET_CLASS_OR_RETURN_NULL(bundle, "android/os/Bundle");
248   TC3_GET_METHOD(bundle, get_boolean, "getBoolean", "(Ljava/lang/String;)Z");
249 
250   // String resources.
251   TC3_GET_CLASS_OR_RETURN_NULL(resources, "android/content/res/Resources");
252   TC3_GET_STATIC_METHOD(resources, get_system, "getSystem",
253                         "()Landroid/content/res/Resources;");
254   TC3_GET_METHOD(resources, get_identifier, "getIdentifier",
255                  "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I");
256   TC3_GET_METHOD(resources, get_string, "getString", "(I)Ljava/lang/String;");
257 #endif
258 
259   return result;
260 }
261 
262 #undef TC3_GET_STATIC_INT_FIELD
263 #undef TC3_GET_STATIC_OBJECT_FIELD_OR_RETURN_NULL
264 #undef TC3_GET_STATIC_METHOD
265 #undef TC3_GET_METHOD
266 #undef TC3_GET_CLASS_OR_RETURN_NULL
267 #undef TC3_GET_OPTIONAL_CLASS
268 #undef TC3_CHECK_JNI_PTR
269 
GetEnv() const270 JNIEnv* JniCache::GetEnv() const {
271   void* env;
272   if (JNI_OK == jvm->GetEnv(&env, JNI_VERSION_1_4)) {
273     return reinterpret_cast<JNIEnv*>(env);
274   } else {
275     TC3_LOG(ERROR) << "JavaICU UniLib used on unattached thread";
276     return nullptr;
277   }
278 }
279 
ExceptionCheckAndClear() const280 bool JniCache::ExceptionCheckAndClear() const {
281   return JniExceptionCheckAndClear(GetEnv());
282 }
283 
ConvertToJavaString(const char * utf8_text,const int utf8_text_size_bytes) const284 StatusOr<ScopedLocalRef<jstring>> JniCache::ConvertToJavaString(
285     const char* utf8_text, const int utf8_text_size_bytes) const {
286   // Create java byte array.
287   JNIEnv* jenv = GetEnv();
288   TC3_ASSIGN_OR_RETURN(ScopedLocalRef<jbyteArray> text_java_utf8,
289                        JniHelper::NewByteArray(jenv, utf8_text_size_bytes));
290 
291   TC3_RETURN_IF_ERROR(JniHelper::SetByteArrayRegion(
292       jenv, text_java_utf8.get(), 0, utf8_text_size_bytes,
293       reinterpret_cast<const jbyte*>(utf8_text)));
294 
295   // Create the string with a UTF-8 charset.
296   TC3_ASSIGN_OR_RETURN(ScopedLocalRef<jstring> result,
297                        JniHelper::NewObject<jstring>(
298                            jenv, string_class.get(), string_init_bytes_charset,
299                            text_java_utf8.get(), string_utf8.get()));
300 
301   return result;
302 }
303 
ConvertToJavaString(StringPiece utf8_text) const304 StatusOr<ScopedLocalRef<jstring>> JniCache::ConvertToJavaString(
305     StringPiece utf8_text) const {
306   return ConvertToJavaString(utf8_text.data(), utf8_text.size());
307 }
308 
ConvertToJavaString(const UnicodeText & text) const309 StatusOr<ScopedLocalRef<jstring>> JniCache::ConvertToJavaString(
310     const UnicodeText& text) const {
311   return ConvertToJavaString(text.data(), text.size_bytes());
312 }
313 
314 }  // namespace libtextclassifier3
315