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