Export lib3 to AOSP (external/libtextclassifier part)

1. Include both annotator (existing one) and actions(new one for smart
   reply and actions)
2. One more model file. actions_suggestions.model is dropped to
  /etc/textclassifier./ It is around 7.5mb for now, we will slim down
  it later.
3. The Java counterpart of the JNI is now moved from frameworks/base
   to here.

Test: atest android.view.textclassifier.TextClassificationManagerTest

Change-Id: Icb2458967ef51efa2952b3eaddefbf1f7b359930
diff --git a/utils/java/jni-base.cc b/utils/java/jni-base.cc
new file mode 100644
index 0000000..8073c5a
--- /dev/null
+++ b/utils/java/jni-base.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/java/jni-base.h"
+
+#include <jni.h>
+#include <type_traits>
+#include <vector>
+
+#include "utils/base/integral_types.h"
+#include "utils/java/scoped_local_ref.h"
+#include "utils/java/string_utils.h"
+#include "utils/memory/mmap.h"
+
+using libtextclassifier3::JStringToUtf8String;
+using libtextclassifier3::ScopedLocalRef;
+
+namespace libtextclassifier3 {
+
+std::string ToStlString(JNIEnv* env, const jstring& str) {
+  std::string result;
+  JStringToUtf8String(env, str, &result);
+  return result;
+}
+
+jint GetFdFromAssetFileDescriptor(JNIEnv* env, jobject afd) {
+  // Get system-level file descriptor from AssetFileDescriptor.
+  ScopedLocalRef<jclass> afd_class(
+      env->FindClass("android/content/res/AssetFileDescriptor"), env);
+  if (afd_class == nullptr) {
+    TC3_LOG(ERROR) << "Couldn't find AssetFileDescriptor.";
+    return reinterpret_cast<jlong>(nullptr);
+  }
+  jmethodID afd_class_getFileDescriptor = env->GetMethodID(
+      afd_class.get(), "getFileDescriptor", "()Ljava/io/FileDescriptor;");
+  if (afd_class_getFileDescriptor == nullptr) {
+    TC3_LOG(ERROR) << "Couldn't find getFileDescriptor.";
+    return reinterpret_cast<jlong>(nullptr);
+  }
+
+  ScopedLocalRef<jclass> fd_class(env->FindClass("java/io/FileDescriptor"),
+                                  env);
+  if (fd_class == nullptr) {
+    TC3_LOG(ERROR) << "Couldn't find FileDescriptor.";
+    return reinterpret_cast<jlong>(nullptr);
+  }
+  jfieldID fd_class_descriptor =
+      env->GetFieldID(fd_class.get(), "descriptor", "I");
+  if (fd_class_descriptor == nullptr) {
+    TC3_LOG(ERROR) << "Couldn't find descriptor.";
+    return reinterpret_cast<jlong>(nullptr);
+  }
+
+  jobject bundle_jfd = env->CallObjectMethod(afd, afd_class_getFileDescriptor);
+  return env->GetIntField(bundle_jfd, fd_class_descriptor);
+}
+
+}  // namespace libtextclassifier3
diff --git a/utils/java/jni-base.h b/utils/java/jni-base.h
new file mode 100644
index 0000000..147ae08
--- /dev/null
+++ b/utils/java/jni-base.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_BASE_H_
+#define LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_BASE_H_
+
+#include <jni.h>
+#include <string>
+
+// When we use a macro as an argument for a macro, an additional level of
+// indirection is needed, if the macro argument is used with # or ##.
+#define TC3_ADD_QUOTES_HELPER(TOKEN) #TOKEN
+#define TC3_ADD_QUOTES(TOKEN) TC3_ADD_QUOTES_HELPER(TOKEN)
+
+#ifndef TC3_PACKAGE_NAME
+#define TC3_PACKAGE_NAME com_google_android_textclassifier
+#endif
+
+#ifndef TC3_PACKAGE_PATH
+#define TC3_PACKAGE_PATH \
+  "com/google/android/textclassifier/"
+#endif
+
+#define TC3_JNI_METHOD_NAME_INTERNAL(package_name, class_name, method_name) \
+  Java_##package_name##_##class_name##_##method_name
+
+#define TC3_JNI_METHOD_PRIMITIVE(return_type, package_name, class_name, \
+                                 method_name)                           \
+  JNIEXPORT return_type JNICALL TC3_JNI_METHOD_NAME_INTERNAL(           \
+      package_name, class_name, method_name)
+
+// The indirection is needed to correctly expand the TC3_PACKAGE_NAME macro.
+// See the explanation near TC3_ADD_QUOTES macro.
+#define TC3_JNI_METHOD2(return_type, package_name, class_name, method_name) \
+  TC3_JNI_METHOD_PRIMITIVE(return_type, package_name, class_name, method_name)
+
+#define TC3_JNI_METHOD(return_type, class_name, method_name) \
+  TC3_JNI_METHOD2(return_type, TC3_PACKAGE_NAME, class_name, method_name)
+
+#define TC3_JNI_METHOD_NAME2(package_name, class_name, method_name) \
+  TC3_JNI_METHOD_NAME_INTERNAL(package_name, class_name, method_name)
+
+#define TC3_JNI_METHOD_NAME(class_name, method_name) \
+  TC3_JNI_METHOD_NAME2(TC3_PACKAGE_NAME, class_name, method_name)
+
+namespace libtextclassifier3 {
+
+template <typename T, typename F>
+std::pair<bool, T> CallJniMethod0(JNIEnv* env, jobject object,
+                                  jclass class_object, F function,
+                                  const std::string& method_name,
+                                  const std::string& return_java_type) {
+  const jmethodID method = env->GetMethodID(class_object, method_name.c_str(),
+                                            ("()" + return_java_type).c_str());
+  if (!method) {
+    return std::make_pair(false, T());
+  }
+  return std::make_pair(true, (env->*function)(object, method));
+}
+
+std::string ToStlString(JNIEnv* env, const jstring& str);
+jint GetFdFromAssetFileDescriptor(JNIEnv* env, jobject afd);
+}  // namespace libtextclassifier3
+
+#endif  // LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_BASE_H_
diff --git a/utils/java/jni-cache.cc b/utils/java/jni-cache.cc
new file mode 100644
index 0000000..cd89e5c
--- /dev/null
+++ b/utils/java/jni-cache.cc
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/java/jni-cache.h"
+
+#include "utils/base/logging.h"
+
+namespace libtextclassifier3 {
+
+JniCache::JniCache(JavaVM* jvm)
+    : jvm(jvm),
+      string_class(nullptr, jvm),
+      string_utf8(nullptr, jvm),
+      pattern_class(nullptr, jvm),
+      matcher_class(nullptr, jvm),
+      locale_class(nullptr, jvm),
+      locale_us(nullptr, jvm),
+      breakiterator_class(nullptr, jvm),
+      integer_class(nullptr, jvm),
+      calendar_class(nullptr, jvm),
+      timezone_class(nullptr, jvm) {}
+
+// The macros below are intended to reduce the boilerplate in Create and avoid
+// easily introduced copy/paste errors.
+#define TC3_CHECK_JNI_PTR(PTR) \
+  TC3_DCHECK(PTR);             \
+  if (!(PTR)) return nullptr;
+
+#define TC3_GET_CLASS(FIELD, NAME)                                       \
+  result->FIELD##_class = MakeGlobalRef(env->FindClass(NAME), env, jvm); \
+  TC3_CHECK_JNI_PTR(result->FIELD##_class)
+
+#define TC3_GET_METHOD(CLASS, FIELD, NAME, SIGNATURE)                 \
+  result->CLASS##_##FIELD =                                           \
+      env->GetMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
+  TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD)
+
+#define TC3_GET_OPTIONAL_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE)       \
+  result->CLASS##_##FIELD =                                                 \
+      env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
+  env->ExceptionClear();
+
+#define TC3_GET_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE)                \
+  result->CLASS##_##FIELD =                                                 \
+      env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
+  TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD)
+
+#define TC3_GET_STATIC_OBJECT_FIELD(CLASS, FIELD, NAME, SIGNATURE)         \
+  const jfieldID CLASS##_##FIELD##_field =                                 \
+      env->GetStaticFieldID(result->CLASS##_class.get(), NAME, SIGNATURE); \
+  TC3_CHECK_JNI_PTR(CLASS##_##FIELD##_field)                               \
+  result->CLASS##_##FIELD =                                                \
+      MakeGlobalRef(env->GetStaticObjectField(result->CLASS##_class.get(), \
+                                              CLASS##_##FIELD##_field),    \
+                    env, jvm);                                             \
+  TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD)
+
+#define TC3_GET_STATIC_INT_FIELD(CLASS, FIELD, NAME)                 \
+  const jfieldID CLASS##_##FIELD##_field =                           \
+      env->GetStaticFieldID(result->CLASS##_class.get(), NAME, "I"); \
+  TC3_CHECK_JNI_PTR(CLASS##_##FIELD##_field)                         \
+  result->CLASS##_##FIELD = env->GetStaticIntField(                  \
+      result->CLASS##_class.get(), CLASS##_##FIELD##_field);         \
+  TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD)
+
+std::unique_ptr<JniCache> JniCache::Create(JNIEnv* env) {
+  if (env == nullptr) {
+    return nullptr;
+  }
+  JavaVM* jvm = nullptr;
+  if (JNI_OK != env->GetJavaVM(&jvm) || jvm == nullptr) {
+    return nullptr;
+  }
+  std::unique_ptr<JniCache> result(new JniCache(jvm));
+
+  // String
+  TC3_GET_CLASS(string, "java/lang/String");
+  TC3_GET_METHOD(string, init_bytes_charset, "<init>",
+                 "([BLjava/lang/String;)V");
+  TC3_GET_METHOD(string, code_point_count, "codePointCount", "(II)I");
+  TC3_GET_METHOD(string, length, "length", "()I");
+  result->string_utf8 = MakeGlobalRef(env->NewStringUTF("UTF-8"), env, jvm);
+  TC3_CHECK_JNI_PTR(result->string_utf8)
+
+  // Pattern
+  TC3_GET_CLASS(pattern, "java/util/regex/Pattern");
+  TC3_GET_STATIC_METHOD(pattern, compile, "compile",
+                        "(Ljava/lang/String;)Ljava/util/regex/Pattern;");
+  TC3_GET_METHOD(pattern, matcher, "matcher",
+                 "(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;");
+
+  // Matcher
+  TC3_GET_CLASS(matcher, "java/util/regex/Matcher");
+  TC3_GET_METHOD(matcher, matches, "matches", "()Z");
+  TC3_GET_METHOD(matcher, find, "find", "()Z");
+  TC3_GET_METHOD(matcher, reset, "reset", "()Ljava/util/regex/Matcher;");
+  TC3_GET_METHOD(matcher, start_idx, "start", "(I)I");
+  TC3_GET_METHOD(matcher, end_idx, "end", "(I)I");
+  TC3_GET_METHOD(matcher, group, "group", "()Ljava/lang/String;");
+  TC3_GET_METHOD(matcher, group_idx, "group", "(I)Ljava/lang/String;");
+
+  // Locale
+  TC3_GET_CLASS(locale, "java/util/Locale");
+  TC3_GET_STATIC_OBJECT_FIELD(locale, us, "US", "Ljava/util/Locale;");
+  TC3_GET_METHOD(locale, init_string, "<init>", "(Ljava/lang/String;)V");
+  TC3_GET_OPTIONAL_STATIC_METHOD(locale, for_language_tag, "forLanguageTag",
+                                 "(Ljava/lang/String;)Ljava/util/Locale;");
+
+  // BreakIterator
+  TC3_GET_CLASS(breakiterator, "java/text/BreakIterator");
+  TC3_GET_STATIC_METHOD(breakiterator, getwordinstance, "getWordInstance",
+                        "(Ljava/util/Locale;)Ljava/text/BreakIterator;");
+  TC3_GET_METHOD(breakiterator, settext, "setText", "(Ljava/lang/String;)V");
+  TC3_GET_METHOD(breakiterator, next, "next", "()I");
+
+  // Integer
+  TC3_GET_CLASS(integer, "java/lang/Integer");
+  TC3_GET_STATIC_METHOD(integer, parse_int, "parseInt",
+                        "(Ljava/lang/String;)I");
+
+  // Calendar.
+  TC3_GET_CLASS(calendar, "java/util/Calendar");
+  TC3_GET_STATIC_METHOD(
+      calendar, get_instance, "getInstance",
+      "(Ljava/util/TimeZone;Ljava/util/Locale;)Ljava/util/Calendar;");
+  TC3_GET_METHOD(calendar, get_first_day_of_week, "getFirstDayOfWeek", "()I");
+  TC3_GET_METHOD(calendar, get_time_in_millis, "getTimeInMillis", "()J");
+  TC3_GET_METHOD(calendar, set_time_in_millis, "setTimeInMillis", "(J)V");
+  TC3_GET_METHOD(calendar, add, "add", "(II)V");
+  TC3_GET_METHOD(calendar, get, "get", "(I)I");
+  TC3_GET_METHOD(calendar, set, "set", "(II)V");
+  TC3_GET_STATIC_INT_FIELD(calendar, zone_offset, "ZONE_OFFSET");
+  TC3_GET_STATIC_INT_FIELD(calendar, dst_offset, "DST_OFFSET");
+  TC3_GET_STATIC_INT_FIELD(calendar, year, "YEAR");
+  TC3_GET_STATIC_INT_FIELD(calendar, month, "MONTH");
+  TC3_GET_STATIC_INT_FIELD(calendar, day_of_year, "DAY_OF_YEAR");
+  TC3_GET_STATIC_INT_FIELD(calendar, day_of_month, "DAY_OF_MONTH");
+  TC3_GET_STATIC_INT_FIELD(calendar, day_of_week, "DAY_OF_WEEK");
+  TC3_GET_STATIC_INT_FIELD(calendar, hour_of_day, "HOUR_OF_DAY");
+  TC3_GET_STATIC_INT_FIELD(calendar, minute, "MINUTE");
+  TC3_GET_STATIC_INT_FIELD(calendar, second, "SECOND");
+  TC3_GET_STATIC_INT_FIELD(calendar, millisecond, "MILLISECOND");
+  TC3_GET_STATIC_INT_FIELD(calendar, sunday, "SUNDAY");
+  TC3_GET_STATIC_INT_FIELD(calendar, monday, "MONDAY");
+  TC3_GET_STATIC_INT_FIELD(calendar, tuesday, "TUESDAY");
+  TC3_GET_STATIC_INT_FIELD(calendar, wednesday, "WEDNESDAY");
+  TC3_GET_STATIC_INT_FIELD(calendar, thursday, "THURSDAY");
+  TC3_GET_STATIC_INT_FIELD(calendar, friday, "FRIDAY");
+  TC3_GET_STATIC_INT_FIELD(calendar, saturday, "SATURDAY");
+
+  // TimeZone.
+  TC3_GET_CLASS(timezone, "java/util/TimeZone");
+  TC3_GET_STATIC_METHOD(timezone, get_timezone, "getTimeZone",
+                        "(Ljava/lang/String;)Ljava/util/TimeZone;");
+
+  return result;
+}
+
+#undef TC3_GET_STATIC_INT_FIELD
+#undef TC3_GET_STATIC_OBJECT_FIELD
+#undef TC3_GET_STATIC_METHOD
+#undef TC3_GET_METHOD
+#undef TC3_GET_CLASS
+#undef TC3_CHECK_JNI_PTR
+
+JNIEnv* JniCache::GetEnv() const {
+  void* env;
+  if (JNI_OK == jvm->GetEnv(&env, JNI_VERSION_1_4)) {
+    return reinterpret_cast<JNIEnv*>(env);
+  } else {
+    TC3_LOG(ERROR) << "JavaICU UniLib used on unattached thread";
+    return nullptr;
+  }
+}
+
+bool JniCache::ExceptionCheckAndClear() const {
+  JNIEnv* env = GetEnv();
+  TC3_CHECK(env != nullptr);
+  const bool result = env->ExceptionCheck();
+  if (result) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+  }
+  return result;
+}
+
+ScopedLocalRef<jstring> JniCache::ConvertToJavaString(
+    const UnicodeText& text) const {
+  // Create java byte array.
+  JNIEnv* jenv = GetEnv();
+  const ScopedLocalRef<jbyteArray> text_java_utf8(
+      jenv->NewByteArray(text.size_bytes()), jenv);
+  if (!text_java_utf8) {
+    return nullptr;
+  }
+
+  jenv->SetByteArrayRegion(text_java_utf8.get(), 0, text.size_bytes(),
+                           reinterpret_cast<const jbyte*>(text.data()));
+
+  // Create the string with a UTF-8 charset.
+  return ScopedLocalRef<jstring>(
+      reinterpret_cast<jstring>(
+          jenv->NewObject(string_class.get(), string_init_bytes_charset,
+                          text_java_utf8.get(), string_utf8.get())),
+      jenv);
+}
+
+}  // namespace libtextclassifier3
diff --git a/utils/java/jni-cache.h b/utils/java/jni-cache.h
new file mode 100644
index 0000000..549d3b4
--- /dev/null
+++ b/utils/java/jni-cache.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_CACHE_H_
+#define LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_CACHE_H_
+
+#include <jni.h>
+#include "utils/java/scoped_global_ref.h"
+#include "utils/java/scoped_local_ref.h"
+#include "utils/utf8/unicodetext.h"
+
+namespace libtextclassifier3 {
+
+// A helper class to cache class and method pointers for calls from JNI to Java.
+// (for implementations such as Java ICU that need to make calls from C++ to
+// Java)
+struct JniCache {
+  static std::unique_ptr<JniCache> Create(JNIEnv* env);
+
+  JNIEnv* GetEnv() const;
+  bool ExceptionCheckAndClear() const;
+
+  JavaVM* jvm = nullptr;
+
+  // java.lang.String
+  ScopedGlobalRef<jclass> string_class;
+  jmethodID string_init_bytes_charset = nullptr;
+  jmethodID string_code_point_count = nullptr;
+  jmethodID string_length = nullptr;
+  ScopedGlobalRef<jstring> string_utf8;
+
+  // java.util.regex.Pattern
+  ScopedGlobalRef<jclass> pattern_class;
+  jmethodID pattern_compile = nullptr;
+  jmethodID pattern_matcher = nullptr;
+
+  // java.util.regex.Matcher
+  ScopedGlobalRef<jclass> matcher_class;
+  jmethodID matcher_matches = nullptr;
+  jmethodID matcher_find = nullptr;
+  jmethodID matcher_reset = nullptr;
+  jmethodID matcher_start_idx = nullptr;
+  jmethodID matcher_end_idx = nullptr;
+  jmethodID matcher_group = nullptr;
+  jmethodID matcher_group_idx = nullptr;
+
+  // java.util.Locale
+  ScopedGlobalRef<jclass> locale_class;
+  ScopedGlobalRef<jobject> locale_us;
+  jmethodID locale_init_string = nullptr;
+  jmethodID locale_for_language_tag = nullptr;
+
+  // java.text.BreakIterator
+  ScopedGlobalRef<jclass> breakiterator_class;
+  jmethodID breakiterator_getwordinstance = nullptr;
+  jmethodID breakiterator_settext = nullptr;
+  jmethodID breakiterator_next = nullptr;
+
+  // java.lang.Integer
+  ScopedGlobalRef<jclass> integer_class;
+  jmethodID integer_parse_int = nullptr;
+
+  // java.util.Calendar
+  ScopedGlobalRef<jclass> calendar_class;
+  jmethodID calendar_get_instance = nullptr;
+  jmethodID calendar_get_first_day_of_week = nullptr;
+  jmethodID calendar_get_time_in_millis = nullptr;
+  jmethodID calendar_set_time_in_millis = nullptr;
+  jmethodID calendar_add = nullptr;
+  jmethodID calendar_get = nullptr;
+  jmethodID calendar_set = nullptr;
+  jint calendar_zone_offset;
+  jint calendar_dst_offset;
+  jint calendar_year;
+  jint calendar_month;
+  jint calendar_day_of_year;
+  jint calendar_day_of_month;
+  jint calendar_day_of_week;
+  jint calendar_hour_of_day;
+  jint calendar_minute;
+  jint calendar_second;
+  jint calendar_millisecond;
+  jint calendar_sunday;
+  jint calendar_monday;
+  jint calendar_tuesday;
+  jint calendar_wednesday;
+  jint calendar_thursday;
+  jint calendar_friday;
+  jint calendar_saturday;
+
+  // java.util.TimeZone
+  ScopedGlobalRef<jclass> timezone_class;
+  jmethodID timezone_get_timezone = nullptr;
+
+  // Helper to convert lib3 UnicodeText to Java strings.
+  ScopedLocalRef<jstring> ConvertToJavaString(const UnicodeText& text) const;
+
+ private:
+  explicit JniCache(JavaVM* jvm);
+};
+
+}  // namespace libtextclassifier3
+
+#endif  // LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_CACHE_H_
diff --git a/utils/java/scoped_global_ref.h b/utils/java/scoped_global_ref.h
new file mode 100644
index 0000000..de0608e
--- /dev/null
+++ b/utils/java/scoped_global_ref.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBTEXTCLASSIFIER_UTILS_JAVA_SCOPED_GLOBAL_REF_H_
+#define LIBTEXTCLASSIFIER_UTILS_JAVA_SCOPED_GLOBAL_REF_H_
+
+#include <jni.h>
+#include <memory>
+#include <type_traits>
+
+#include "utils/base/logging.h"
+
+namespace libtextclassifier3 {
+
+// A deleter to be used with std::unique_ptr to delete JNI global references.
+class GlobalRefDeleter {
+ public:
+  GlobalRefDeleter() : jvm_(nullptr) {}
+
+  // Style guide violating implicit constructor so that the GlobalRefDeleter
+  // is implicitly constructed from the second argument to ScopedGlobalRef.
+  GlobalRefDeleter(JavaVM* jvm) : jvm_(jvm) {}  // NOLINT(runtime/explicit)
+
+  GlobalRefDeleter(const GlobalRefDeleter& orig) = default;
+
+  // Copy assignment to allow move semantics in ScopedGlobalRef.
+  GlobalRefDeleter& operator=(const GlobalRefDeleter& rhs) {
+    TC3_CHECK_EQ(jvm_, rhs.jvm_);
+    return *this;
+  }
+
+  // The delete operator.
+  void operator()(jobject object) const {
+    JNIEnv* env;
+    if (object != nullptr && jvm_ != nullptr &&
+        JNI_OK ==
+            jvm_->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
+      env->DeleteGlobalRef(object);
+    }
+  }
+
+ private:
+  // The jvm_ stashed to use for deletion.
+  JavaVM* const jvm_;
+};
+
+// A smart pointer that deletes a JNI global reference when it goes out
+// of scope. Usage is:
+// ScopedGlobalRef<jobject> scoped_global(env->JniFunction(), jvm);
+template <typename T>
+using ScopedGlobalRef =
+    std::unique_ptr<typename std::remove_pointer<T>::type, GlobalRefDeleter>;
+
+// A helper to create global references. Assumes the object has a local
+// reference, which it deletes.
+template <typename T>
+ScopedGlobalRef<T> MakeGlobalRef(T object, JNIEnv* env, JavaVM* jvm) {
+  const jobject global_object = env->NewGlobalRef(object);
+  env->DeleteLocalRef(object);
+  return ScopedGlobalRef<T>(reinterpret_cast<T>(global_object), jvm);
+}
+
+}  // namespace libtextclassifier3
+
+#endif  // LIBTEXTCLASSIFIER_UTILS_JAVA_SCOPED_GLOBAL_REF_H_
diff --git a/utils/java/scoped_local_ref.h b/utils/java/scoped_local_ref.h
new file mode 100644
index 0000000..f439c45
--- /dev/null
+++ b/utils/java/scoped_local_ref.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBTEXTCLASSIFIER_UTILS_JAVA_SCOPED_LOCAL_REF_H_
+#define LIBTEXTCLASSIFIER_UTILS_JAVA_SCOPED_LOCAL_REF_H_
+
+#include <jni.h>
+#include <memory>
+#include <type_traits>
+
+#include "utils/base/logging.h"
+
+namespace libtextclassifier3 {
+
+// A deleter to be used with std::unique_ptr to delete JNI local references.
+class LocalRefDeleter {
+ public:
+  LocalRefDeleter() : env_(nullptr) {}
+
+  // Style guide violating implicit constructor so that the LocalRefDeleter
+  // is implicitly constructed from the second argument to ScopedLocalRef.
+  LocalRefDeleter(JNIEnv* env) : env_(env) {}  // NOLINT(runtime/explicit)
+
+  LocalRefDeleter(const LocalRefDeleter& orig) = default;
+
+  // Copy assignment to allow move semantics in ScopedLocalRef.
+  LocalRefDeleter& operator=(const LocalRefDeleter& rhs) {
+    // As the deleter and its state are thread-local, ensure the envs
+    // are consistent but do nothing.
+    TC3_CHECK_EQ(env_, rhs.env_);
+    return *this;
+  }
+
+  // The delete operator.
+  void operator()(jobject object) const {
+    if (env_) {
+      env_->DeleteLocalRef(object);
+    }
+  }
+
+ private:
+  // The env_ stashed to use for deletion. Thread-local, don't share!
+  JNIEnv* const env_;
+};
+
+// A smart pointer that deletes a JNI local reference when it goes out
+// of scope. Usage is:
+// ScopedLocalRef<jobject> scoped_local(env->JniFunction(), env);
+//
+// Note that this class is not thread-safe since it caches JNIEnv in
+// the deleter. Do not use the same jobject across different threads.
+template <typename T>
+using ScopedLocalRef =
+    std::unique_ptr<typename std::remove_pointer<T>::type, LocalRefDeleter>;
+
+}  // namespace libtextclassifier3
+
+#endif  // LIBTEXTCLASSIFIER_UTILS_JAVA_SCOPED_LOCAL_REF_H_
diff --git a/utils/java/string_utils.cc b/utils/java/string_utils.cc
new file mode 100644
index 0000000..6d4a5d7
--- /dev/null
+++ b/utils/java/string_utils.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/java/string_utils.h"
+
+#include "utils/base/logging.h"
+
+namespace libtextclassifier3 {
+
+bool JStringToUtf8String(JNIEnv* env, const jstring& jstr,
+                         std::string* result) {
+  if (jstr == nullptr) {
+    *result = std::string();
+    return false;
+  }
+
+  jclass string_class = env->FindClass("java/lang/String");
+  if (!string_class) {
+    TC3_LOG(ERROR) << "Can't find String class";
+    return false;
+  }
+
+  jmethodID get_bytes_id =
+      env->GetMethodID(string_class, "getBytes", "(Ljava/lang/String;)[B");
+
+  jstring encoding = env->NewStringUTF("UTF-8");
+  jbyteArray array = reinterpret_cast<jbyteArray>(
+      env->CallObjectMethod(jstr, get_bytes_id, encoding));
+
+  jbyte* const array_bytes = env->GetByteArrayElements(array, JNI_FALSE);
+  int length = env->GetArrayLength(array);
+
+  *result = std::string(reinterpret_cast<char*>(array_bytes), length);
+
+  // Release the array.
+  env->ReleaseByteArrayElements(array, array_bytes, JNI_ABORT);
+  env->DeleteLocalRef(array);
+  env->DeleteLocalRef(string_class);
+  env->DeleteLocalRef(encoding);
+
+  return true;
+}
+
+}  // namespace libtextclassifier3
diff --git a/utils/java/string_utils.h b/utils/java/string_utils.h
new file mode 100644
index 0000000..c4fd97a
--- /dev/null
+++ b/utils/java/string_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBTEXTCLASSIFIER_UTILS_JAVA_STRING_UTILS_H_
+#define LIBTEXTCLASSIFIER_UTILS_JAVA_STRING_UTILS_H_
+
+#include <jni.h>
+#include <string>
+
+namespace libtextclassifier3 {
+
+bool JStringToUtf8String(JNIEnv* env, const jstring& jstr, std::string* result);
+
+}  // namespace libtextclassifier3
+
+#endif  // LIBTEXTCLASSIFIER_UTILS_JAVA_STRING_UTILS_H_