Merge "Move ICU4C regex usage from libcore/"
diff --git a/android_icu4j/Android.bp b/android_icu4j/Android.bp
index 5da0613..9725b81 100644
--- a/android_icu4j/Android.bp
+++ b/android_icu4j/Android.bp
@@ -29,10 +29,77 @@
     visibility: [
         "//libcore:__subpackages__",
     ],
-    srcs: ["src/main/java/**/*.java"],
+    srcs: [
+        ":android_icu4j_repackaged_src_files",
+        ":libcore_icu_bridge_src_files",
+    ],
     path: "src/main/java",
 }
 
+filegroup {
+    name: "android_icu4j_repackaged_src_files",
+    srcs: ["src/main/java/android/icu/**/*.java"],
+    path: "src/main/java",
+}
+
+// The files contains Android-specific codes to expose intra-core APIs
+// from ICU4J/ICU4C to libcore. The package is com.android.icu.* and should not
+// expose any public APIs.
+filegroup {
+    name: "libcore_icu_bridge_src_files",
+    srcs: ["src/main/java/com/android/icu/**/*.java"],
+    path: "src/main/java",
+}
+
+// Rule generating resource lib for android_icu4j.
+// In the downstream branch master-icu-dev, the resource files are generated.
+java_library {
+    name: "android_icu4j_resources_lib",
+    visibility: [
+        "//libcore",
+    ],
+    java_resource_dirs: ["resources"],
+    sdk_version: "none",
+    system_modules: "none",
+}
+
+// Same as android_icu4j_resources_lib but compiling against core_current sdk
+// in order to avoid using non-public API from core-libart and core-oj
+// because core-icu4j will be in a different i18n APEX module.
+
+java_library {
+    name: "android_icu4j_resources_lib_sdk_core_current",
+    visibility: [
+        "//libcore",
+    ],
+    java_resource_dirs: ["resources"],
+    sdk_version: "core_current",
+}
+
+// core-repackaged-icu4j contains only the repackaged ICU4J that does not
+// use any internal or android specific code. If it ever did then it could depend on
+// art-module-intra-core-api-stubs-system-modules (a superset) instead.
+// It is important that core-icu4j is restricted to only use stable APIs from the ART module
+// since it is in a different APEX module that can be updated independently.
+java_library_static {
+    name: "core-repackaged-icu4j",
+    installable: false,
+    srcs: [":android_icu4j_repackaged_src_files"],
+    // The resource files are generated in the downstream branch master-icu-dev.
+    java_resource_dirs: ["resources"],
+
+    sdk_version: "none",
+    system_modules: "art-module-public-api-stubs-system-modules",
+
+    dxflags: ["--core-library"],
+    errorprone: {
+        javacflags: [
+            "-Xep:MissingOverride:OFF", // Ignore missing @Override.
+            "-Xep:ConstantOverflow:WARN", // Known constant overflow in SplittableRandom
+        ],
+    },
+}
+
 // A separated core library that contains ICU4J because ICU4J will be in a different APEX module,
 // not in ART module.
 java_library {
@@ -45,26 +112,15 @@
     installable: true,
     hostdex: true,
 
-    srcs: [":android_icu4j_src_files"],
+    srcs: [":libcore_icu_bridge_src_files"],
+    static_libs: ["core-repackaged-icu4j"],
 
-    // The resource files are generated in the downstream branch master-icu-dev.
-    java_resource_dirs: ["resources"],
-
-    // We use art-module-public-api-stubs-system-modules when compiling core-icu4j as ICU4J does not
-    // use any internal or android specific code. If it ever did then it could depend on
-    // art-module-intra-core-api-stubs-system-modules (a superset) instead.
     // It is important that core-icu4j is restricted to only use stable APIs from the ART module
     // since it is in a different APEX module that can be updated independently.
     sdk_version: "none",
-    system_modules: "art-module-public-api-stubs-system-modules",
+    system_modules: "art-module-intra-core-api-stubs-system-modules",
 
     dxflags: ["--core-library"],
-    errorprone: {
-        javacflags: [
-            "-Xep:MissingOverride:OFF", // Ignore missing @Override.
-            "-Xep:ConstantOverflow:WARN", // Known constant overflow in SplittableRandom
-        ],
-    },
 }
 
 //
diff --git a/android_icu4j/api/intra/current-api.txt b/android_icu4j/api/intra/current-api.txt
index 1d0dd77..4911658 100644
--- a/android_icu4j/api/intra/current-api.txt
+++ b/android_icu4j/api/intra/current-api.txt
@@ -181,3 +181,26 @@
 
 }
 
+package com.android.icu.util.regex {
+
+  public class NativeMatcher {
+    method public static com.android.icu.util.regex.NativeMatcher create(com.android.icu.util.regex.NativePattern);
+    method public boolean find(int, int[]);
+    method public boolean findNext(int[]);
+    method public int getMatchedGroupIndex(String);
+    method public int groupCount();
+    method public boolean hitEnd();
+    method public boolean lookingAt(int[]);
+    method public boolean matches(int[]);
+    method public boolean requireEnd();
+    method public void setInput(String, int, int);
+    method public void useAnchoringBounds(boolean);
+    method public void useTransparentBounds(boolean);
+  }
+
+  public class NativePattern {
+    method public static com.android.icu.util.regex.NativePattern create(String, int);
+  }
+
+}
+
diff --git a/android_icu4j/src/main/java/com/android/icu/util/regex/NativeMatcher.java b/android_icu4j/src/main/java/com/android/icu/util/regex/NativeMatcher.java
new file mode 100644
index 0000000..9165b3e
--- /dev/null
+++ b/android_icu4j/src/main/java/com/android/icu/util/regex/NativeMatcher.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package com.android.icu.util.regex;
+
+import libcore.util.NativeAllocationRegistry;
+
+public class NativeMatcher {
+
+    private static final NativeAllocationRegistry REGISTRY = NativeAllocationRegistry
+        .createMalloced(NativeMatcher.class.getClassLoader(), getNativeFinalizer());
+
+    /*
+     * @ReachabilitySensitive ensures that this and NativePattern remain reachable while we're using
+     * nativePattern.address directly.
+     */
+    @dalvik.annotation.optimization.ReachabilitySensitive
+    private final NativePattern nativePattern;
+    @dalvik.annotation.optimization.ReachabilitySensitive
+    private final long address;
+
+    @libcore.api.IntraCoreApi
+    public static NativeMatcher create(NativePattern pattern) {
+        return new NativeMatcher(pattern);
+    }
+
+    private NativeMatcher(NativePattern pattern) {
+        nativePattern = pattern;
+        address = openImpl(pattern.address);
+        REGISTRY.registerNativeAllocation(this, address);
+    }
+
+    @libcore.api.IntraCoreApi
+    public int getMatchedGroupIndex(String groupName) {
+        return getMatchedGroupIndexImpl(nativePattern.address, groupName);
+    }
+
+    @libcore.api.IntraCoreApi
+    public boolean find(int startIndex, int[] offsets) {
+        return findImpl(address, startIndex, offsets);
+    }
+
+    @libcore.api.IntraCoreApi
+    public boolean findNext(int[] offsets) {
+        return findNextImpl(address, offsets);
+    }
+
+    @libcore.api.IntraCoreApi
+    public int groupCount() {
+        return groupCountImpl(address);
+    }
+
+    @libcore.api.IntraCoreApi
+    public boolean hitEnd() {
+        return hitEndImpl(address);
+    }
+
+    @libcore.api.IntraCoreApi
+    public boolean lookingAt(int[] offsets) {
+        return lookingAtImpl(address, offsets);
+    }
+
+    @libcore.api.IntraCoreApi
+    public boolean matches(int[] offsets) {
+        return matchesImpl(address, offsets);
+    }
+
+    @libcore.api.IntraCoreApi
+    public boolean requireEnd() {
+        return requireEndImpl(address);
+    }
+
+    @libcore.api.IntraCoreApi
+    public void setInput(String input, int start, int end) {
+        setInputImpl(address, input, start, end);
+    }
+
+    @libcore.api.IntraCoreApi
+    public void useAnchoringBounds(boolean value) {
+        useAnchoringBoundsImpl(address, value);
+    }
+
+    @libcore.api.IntraCoreApi
+    public void useTransparentBounds(boolean value) {
+        useTransparentBoundsImpl(address, value);
+    }
+
+    private static native long openImpl(long patternAddr);
+    private static native int getMatchedGroupIndexImpl(long patternAddr, String name);
+    private static native boolean findImpl(long addr, int startIndex, int[] offsets);
+    private static native boolean findNextImpl(long addr, int[] offsets);
+    private static native int groupCountImpl(long addr);
+    private static native boolean hitEndImpl(long addr);
+    private static native boolean lookingAtImpl(long addr, int[] offsets);
+    private static native boolean matchesImpl(long addr, int[] offsets);
+    private static native boolean requireEndImpl(long addr);
+    private static native void setInputImpl(long addr, String input, int start, int end);
+    private static native void useAnchoringBoundsImpl(long addr, boolean value);
+    private static native void useTransparentBoundsImpl(long addr, boolean value);
+
+    /**
+     * @return address of a native function of type <code>void f(void* nativePtr)</code>
+     *         used to free this kind of native allocation
+     */
+    private static native long getNativeFinalizer();
+
+}
diff --git a/android_icu4j/src/main/java/com/android/icu/util/regex/NativePattern.java b/android_icu4j/src/main/java/com/android/icu/util/regex/NativePattern.java
new file mode 100644
index 0000000..2d0a6ca
--- /dev/null
+++ b/android_icu4j/src/main/java/com/android/icu/util/regex/NativePattern.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package com.android.icu.util.regex;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Provide an entry point to use ICU4C icu::RegexPattern.
+ */
+@libcore.api.IntraCoreApi
+public class NativePattern {
+
+    private static final NativeAllocationRegistry REGISTRY = NativeAllocationRegistry
+        .createMalloced(NativePattern.class.getClassLoader(), getNativeFinalizer());
+
+    @dalvik.annotation.optimization.ReachabilitySensitive
+    final long address;
+
+    @libcore.api.IntraCoreApi
+    public static NativePattern create(String pattern, int flags) {
+        return new NativePattern(pattern, flags);
+    }
+
+    private NativePattern(String pattern, int flags) {
+        address = compileImpl(pattern, flags);
+        REGISTRY.registerNativeAllocation(this, address);
+    }
+
+    /**
+     * @return native address of the native allocation.
+     */
+    private static native long compileImpl(String pattern, int flags);
+
+    /**
+     * @return address of a native function of type <code>void f(void* nativePtr)</code>
+     *         used to free this kind of native allocation
+     */
+    private static native long getNativeFinalizer();
+
+}
diff --git a/android_icu4j/src/main/java/com/android/icu/util/regex/TEST_MAPPING b/android_icu4j/src/main/java/com/android/icu/util/regex/TEST_MAPPING
new file mode 100644
index 0000000..a2bd15f
--- /dev/null
+++ b/android_icu4j/src/main/java/com/android/icu/util/regex/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "postsubmit": [
+    {
+      "name": "CtsLibcoreTestCases",
+      "options": [
+        {
+          "include-filter": "org.apache.harmony.regex.tests.java.util.regex"
+        },
+        {
+          "include-filter": "libcore.java.util.regex"
+        },
+        {
+          "include-filter": "org.apache.harmony.tests.java.util.regex"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/android_icu4j/src/main/jni/Android.bp b/android_icu4j/src/main/jni/Android.bp
new file mode 100644
index 0000000..15eae8a
--- /dev/null
+++ b/android_icu4j/src/main/jni/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2019 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.
+//
+package {
+    default_visibility: [
+        "//art/build/apex",
+    ],
+}
+
+cc_library_shared {
+    name: "libicu_jni",
+    host_supported: true,
+    shared_libs: [
+        "libbase",
+        "libicuuc",
+        "libicui18n",
+        "liblog",
+        "libnativehelper",
+    ],
+    srcs: [
+        "*.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    cppflags: ["-DU_USING_ICU_NAMESPACE=0"],
+    target: {
+        android: {
+            cflags: [
+                // -DANDROID_LINK_SHARED_ICU4C to enable access to the full ICU4C.
+                // See external/icu/android_icu4c/include/uconfig_local.h
+                // for more information.
+                "-DANDROID_LINK_SHARED_ICU4C",
+            ],
+        },
+    },
+}
diff --git a/android_icu4j/src/main/jni/IcuUtilities.cpp b/android_icu4j/src/main/jni/IcuUtilities.cpp
new file mode 100644
index 0000000..53c7617
--- /dev/null
+++ b/android_icu4j/src/main/jni/IcuUtilities.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 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 <nativehelper/JNIHelp.h>
+
+#include "IcuUtilities.h"
+
+#include "JniConstants.h"
+#include "unicode/strenum.h"
+#include "unicode/ustring.h"
+#include "unicode/uloc.h"
+
+bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error) {
+  if (U_SUCCESS(error)) {
+    return false;
+  }
+  const char* exceptionClass = "java/lang/RuntimeException";
+  if (error == U_ILLEGAL_ARGUMENT_ERROR) {
+    exceptionClass = "java/lang/IllegalArgumentException";
+  } else if (error == U_INDEX_OUTOFBOUNDS_ERROR || error == U_BUFFER_OVERFLOW_ERROR) {
+    exceptionClass = "java/lang/ArrayIndexOutOfBoundsException";
+  } else if (error == U_UNSUPPORTED_ERROR) {
+    exceptionClass = "java/lang/UnsupportedOperationException";
+  } else if (error == U_FORMAT_INEXACT_ERROR) {
+    exceptionClass = "java/lang/ArithmeticException";
+  }
+  jniThrowExceptionFmt(env, exceptionClass, "%s failed: %s", function, u_errorName(error));
+  return true;
+}
diff --git a/android_icu4j/src/main/jni/IcuUtilities.h b/android_icu4j/src/main/jni/IcuUtilities.h
new file mode 100644
index 0000000..451dc32
--- /dev/null
+++ b/android_icu4j/src/main/jni/IcuUtilities.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2013 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 ICU_UTILITIES_H_included
+#define ICU_UTILITIES_H_included
+
+#include "jni.h"
+#include "unicode/utypes.h" // For UErrorCode.
+
+bool maybeThrowIcuException(JNIEnv* env, const char* function, UErrorCode error);
+
+#endif  // ICU_UTILITIES_H_included
diff --git a/android_icu4j/src/main/jni/JniConstants.cpp b/android_icu4j/src/main/jni/JniConstants.cpp
new file mode 100644
index 0000000..88126e8
--- /dev/null
+++ b/android_icu4j/src/main/jni/JniConstants.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define LOG_TAG "JniConstants"
+
+#include "JniConstants.h"
+
+#include <atomic>
+#include <mutex>
+#include <stdlib.h>
+
+#include <log/log.h>
+
+namespace {
+
+jclass findClass(JNIEnv* env, const char* name) {
+    jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(name)));
+    if (result == NULL) {
+        ALOGE("failed to find class '%s'", name);
+        abort();
+    }
+    return result;
+}
+
+static std::mutex g_constants_mutex;
+
+// Flag indicating whether cached constants are valid
+static bool g_constants_valid = false;
+
+jclass patternSyntaxExceptionClass;
+
+// EnsureJniConstantsInitialized initializes cached constants. It should be
+// called before returning a heap object from the cache to ensure cache is
+// initialized. This pattern is only necessary because if a process finishes one
+// runtime and starts another then JNI_OnLoad may not be called.
+void EnsureJniConstantsInitialized(JNIEnv* env) {
+    if (g_constants_valid) {
+        return;
+    }
+
+    std::lock_guard guard(g_constants_mutex);
+    if (g_constants_valid) {
+        return;
+    }
+
+    patternSyntaxExceptionClass = findClass(env, "java/util/regex/PatternSyntaxException");
+
+    g_constants_valid = true;
+}
+
+}  // namespace
+
+void JniConstants::Initialize(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+}
+
+jclass JniConstants::GetPatternSyntaxExceptionClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return patternSyntaxExceptionClass;
+}
+
+void JniConstants::Invalidate() {
+    std::lock_guard guard(g_constants_mutex);
+    g_constants_valid = false;
+}
\ No newline at end of file
diff --git a/android_icu4j/src/main/jni/JniConstants.h b/android_icu4j/src/main/jni/JniConstants.h
new file mode 100644
index 0000000..18275cb
--- /dev/null
+++ b/android_icu4j/src/main/jni/JniConstants.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#pragma once
+
+#include <jni.h>
+
+/**
+ * A cache to avoid calling FindClass at runtime.
+ */
+struct JniConstants {
+    // Initialized cached heap objects. This should be called in JNI_OnLoad.
+    static void Initialize(JNIEnv* env);
+
+    // Invalidate cached heap objects. This should be called in JNI_OnUnload.
+    static void Invalidate();
+
+    static jclass GetPatternSyntaxExceptionClass(JNIEnv* env);
+};
\ No newline at end of file
diff --git a/android_icu4j/src/main/jni/Register.cpp b/android_icu4j/src/main/jni/Register.cpp
new file mode 100644
index 0000000..a66d59a
--- /dev/null
+++ b/android_icu4j/src/main/jni/Register.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define LOG_TAG "Register"
+
+#include <stdlib.h>
+#include <nativehelper/ScopedLocalFrame.h>
+#include <log/log.h>
+
+#include "JniConstants.h"
+
+// ART calls this on startup, so we can statically register all our native methods.
+jint JNI_OnLoad(JavaVM* vm, void*) {
+    ALOGV("libicu_jni JNI_OnLoad");
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        ALOGE("JavaVM::GetEnv() failed");
+        abort();
+    }
+
+    ScopedLocalFrame localFrame(env);
+
+#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
+    REGISTER(register_com_android_icu_util_regex_NativePattern);
+    REGISTER(register_com_android_icu_util_regex_NativeMatcher);
+#undef REGISTER
+
+    JniConstants::Initialize(env);
+    return JNI_VERSION_1_6;
+}
+
+// ART calls this on shutdown, do any global cleanup here.
+// -- Very important if we restart multiple ART runtimes in the same process to reset the state.
+void JNI_OnUnload(JavaVM*, void*) {
+    // Don't use the JavaVM in this method. ART only calls this once all threads are
+    // unregistered.
+    ALOGV("libicu_jni JNI_OnUnload");
+    JniConstants::Invalidate();
+}
diff --git a/android_icu4j/src/main/jni/ScopedJavaUnicodeString.h b/android_icu4j/src/main/jni/ScopedJavaUnicodeString.h
new file mode 100644
index 0000000..a30aa7e
--- /dev/null
+++ b/android_icu4j/src/main/jni/ScopedJavaUnicodeString.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 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 SCOPED_JAVA_UNICODE_STRING_H_included
+#define SCOPED_JAVA_UNICODE_STRING_H_included
+
+#include <nativehelper/JNIHelp.h>
+#include "unicode/unistr.h"
+
+// A smart pointer that provides access to an ICU UnicodeString given a JNI
+// jstring. We give ICU a direct pointer to the characters on the Java heap.
+// It's clever enough to copy-on-write if necessary.
+class ScopedJavaUnicodeString {
+ public:
+  ScopedJavaUnicodeString(JNIEnv* env, jstring s) : mEnv(env), mString(s) {
+    if (s == NULL) {
+      jniThrowNullPointerException(mEnv, NULL);
+    } else {
+      mChars = env->GetStringChars(mString, NULL);
+      const int32_t charCount = env->GetStringLength(mString);
+      mUnicodeString.setTo(false, mChars, charCount);
+    }
+  }
+
+  ~ScopedJavaUnicodeString() {
+    if (mString != NULL) {
+      mEnv->ReleaseStringChars(mString, mChars);
+    }
+  }
+
+  bool valid() const {
+    return (mString != NULL);
+  }
+
+  const icu::UnicodeString& unicodeString() const {
+    return mUnicodeString;
+  }
+
+  icu::UnicodeString& unicodeString() {
+    return mUnicodeString;
+  }
+
+ private:
+  JNIEnv* mEnv;
+  jstring mString;
+  const jchar* mChars;
+  icu::UnicodeString mUnicodeString;
+
+  // Disallow copy and assignment.
+  ScopedJavaUnicodeString(const ScopedJavaUnicodeString&);
+  void operator=(const ScopedJavaUnicodeString&);
+};
+
+#endif  // SCOPED_JAVA_UNICODE_STRING_H_included
diff --git a/android_icu4j/src/main/jni/TEST_MAPPING b/android_icu4j/src/main/jni/TEST_MAPPING
new file mode 100644
index 0000000..18d7bf5
--- /dev/null
+++ b/android_icu4j/src/main/jni/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "external/icu/android_icu4j/src/main/java/com/android/icu/util/regex"
+    }
+  ]
+}
diff --git a/android_icu4j/src/main/jni/com_android_icu_util_regex_NativeMatcher.cpp b/android_icu4j/src/main/jni/com_android_icu_util_regex_NativeMatcher.cpp
new file mode 100644
index 0000000..8e5e200
--- /dev/null
+++ b/android_icu4j/src/main/jni/com_android_icu_util_regex_NativeMatcher.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define LOG_TAG "NativeMatcher"
+
+#include <memory>
+#include <stdlib.h>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/jni_macros.h>
+
+#include <android-base/logging.h>
+#include "IcuUtilities.h"
+#include "ScopedJavaUnicodeString.h"
+#include "unicode/parseerr.h"
+#include "unicode/regex.h"
+
+// ICU documentation: http://icu-project.org/apiref/icu4c/classRegexMatcher.html
+
+/**
+ * Encapsulates an instance of ICU4C's RegexMatcher class along with a copy of
+ * the input it's currently operating on in the native heap.
+ *
+ * Rationale: We choose to make a copy here because it turns out to be a lot
+ * cheaper when a moving GC and/or string compression is enabled. This is
+ * because env->GetStringChars() always copies in this scenario. This becomes
+ * especially bad when the String in question is long and/or contains a large
+ * number of matches.
+ *
+ * Drawbacks: The native allocation associated with this class is no longer
+ * fixed size, so we're effectively lying to the NativeAllocationRegistry about
+ * the size of the object(s) we're allocating on the native heap. The peak
+ * memory usage doesn't change though, given that GetStringChars would have
+ * made an allocation of precisely the same size.
+ */
+class MatcherState {
+public:
+    MatcherState(icu::RegexMatcher* matcher) :
+        mMatcher(matcher),
+        mUChars(nullptr),
+        mUText(nullptr),
+        mStatus(U_ZERO_ERROR) {
+    }
+
+    bool updateInput(JNIEnv* env, jstring input) {
+        // First, close the UText struct, since we're about to allocate a new one.
+        if (mUText != nullptr) {
+            utext_close(mUText);
+            mUText = nullptr;
+        }
+
+        // Then delete the UChar* associated with the UText struct..
+        mUChars.reset(nullptr);
+
+        // TODO: We should investigate whether we can avoid an additional copy
+        // in the native heap when is_copy == JNI_TRUE. The problem with doing
+        // that is that we might call ReleaseStringChars with a different
+        // JNIEnv* on a different downcall. This is currently safe as
+        // implemented in ART, but is unlikely to be portable and the spec stays
+        // silent on the matter.
+        ScopedStringChars inputChars(env, input);
+        if (inputChars.get() == nullptr) {
+            // There will be an exception pending if we get here.
+            return false;
+        }
+
+        // Make a copy of |input| on the native heap. This copy will be live
+        // until the next call to updateInput or close.
+        mUChars.reset(new (std::nothrow) UChar[inputChars.size()]);
+        if (mUChars.get() == nullptr) {
+            env->ThrowNew(env->FindClass("Ljava/lang/OutOfMemoryError;"), "Out of memory");
+            return false;
+        }
+
+        static_assert(sizeof(UChar) == sizeof(jchar), "sizeof(Uchar) != sizeof(jchar)");
+        memcpy(mUChars.get(), inputChars.get(), inputChars.size() * sizeof(jchar));
+
+        // Reset any errors that might have occurred on previous patches.
+        mStatus = U_ZERO_ERROR;
+        mUText = utext_openUChars(nullptr, mUChars.get(), inputChars.size(), &mStatus);
+        if (mUText == nullptr) {
+            CHECK(maybeThrowIcuException(env, "utext_openUChars", mStatus));
+            return false;
+        }
+
+        // It is an error for ICU to have returned a non-null mUText but to
+        // still have indicated an error.
+        CHECK(U_SUCCESS(mStatus));
+
+        mMatcher->reset(mUText);
+        return true;
+    }
+
+    ~MatcherState() {
+        if (mUText != nullptr) {
+            utext_close(mUText);
+        }
+    }
+
+    icu::RegexMatcher* matcher() {
+        return mMatcher.get();
+    }
+
+    UErrorCode& status() {
+        return mStatus;
+    }
+
+    void updateOffsets(JNIEnv* env, jintArray javaOffsets) {
+        ScopedIntArrayRW offsets(env, javaOffsets);
+        if (offsets.get() == NULL) {
+            return;
+        }
+
+        for (size_t i = 0, groupCount = mMatcher->groupCount(); i <= groupCount; ++i) {
+            offsets[2*i + 0] = mMatcher->start(i, mStatus);
+            offsets[2*i + 1] = mMatcher->end(i, mStatus);
+        }
+    }
+
+private:
+    std::unique_ptr<icu::RegexMatcher> mMatcher;
+    std::unique_ptr<UChar[]> mUChars;
+    UText* mUText;
+    UErrorCode mStatus;
+
+    // Disallow copy and assignment.
+    MatcherState(const MatcherState&);
+    void operator=(const MatcherState&);
+};
+
+static inline MatcherState* toMatcherState(jlong address) {
+    return reinterpret_cast<MatcherState*>(static_cast<uintptr_t>(address));
+}
+
+static void NativeMatcher_free(void* address) {
+    MatcherState* state = reinterpret_cast<MatcherState*>(address);
+    delete state;
+}
+
+static jlong NativeMatcher_getNativeFinalizer(JNIEnv*, jclass) {
+    return reinterpret_cast<jlong>(&NativeMatcher_free);
+}
+
+static jboolean NativeMatcher_findImpl(JNIEnv* env, jclass, jlong addr, jint startIndex, jintArray offsets) {
+    MatcherState* state = toMatcherState(addr);
+    UBool result = state->matcher()->find(startIndex, state->status());
+    if (result) {
+        state->updateOffsets(env, offsets);
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static jboolean NativeMatcher_findNextImpl(JNIEnv* env, jclass, jlong addr, jintArray offsets) {
+    MatcherState* state = toMatcherState(addr);
+    UBool result = state->matcher()->find();
+    if (result) {
+        state->updateOffsets(env, offsets);
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static jint NativeMatcher_groupCountImpl(JNIEnv*, jclass, jlong addr) {
+    MatcherState* state = toMatcherState(addr);
+    return state->matcher()->groupCount();
+}
+
+static jboolean NativeMatcher_hitEndImpl(JNIEnv*, jclass, jlong addr) {
+    MatcherState* state = toMatcherState(addr);
+    if (state->matcher()->hitEnd() != 0) {
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static jboolean NativeMatcher_lookingAtImpl(JNIEnv* env, jclass, jlong addr, jintArray offsets) {
+    MatcherState* state = toMatcherState(addr);
+    UBool result = state->matcher()->lookingAt(state->status());
+    if (result) {
+        state->updateOffsets(env, offsets);
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static jboolean NativeMatcher_matchesImpl(JNIEnv* env, jclass, jlong addr, jintArray offsets) {
+    MatcherState* state = toMatcherState(addr);
+    UBool result = state->matcher()->matches(state->status());
+    if (result) {
+        state->updateOffsets(env, offsets);
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static jlong NativeMatcher_openImpl(JNIEnv* env, jclass, jlong patternAddr) {
+    icu::RegexPattern* pattern = reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(patternAddr));
+    UErrorCode status = U_ZERO_ERROR;
+    icu::RegexMatcher* result = pattern->matcher(status);
+    if (maybeThrowIcuException(env, "RegexPattern::matcher", status)) {
+        return 0;
+    }
+
+    return reinterpret_cast<uintptr_t>(new MatcherState(result));
+}
+
+static jboolean NativeMatcher_requireEndImpl(JNIEnv*, jclass, jlong addr) {
+    MatcherState* state = toMatcherState(addr);
+    if (state->matcher()->requireEnd() != 0) {
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static void NativeMatcher_setInputImpl(JNIEnv* env, jclass, jlong addr, jstring javaText, jint start, jint end) {
+    MatcherState* state = toMatcherState(addr);
+    if (state->updateInput(env, javaText)) {
+        state->matcher()->region(start, end, state->status());
+    }
+}
+
+static void NativeMatcher_useAnchoringBoundsImpl(JNIEnv*, jclass, jlong addr, jboolean value) {
+    MatcherState* state = toMatcherState(addr);
+    state->matcher()->useAnchoringBounds(value);
+}
+
+static void NativeMatcher_useTransparentBoundsImpl(JNIEnv*, jclass, jlong addr, jboolean value) {
+    MatcherState* state = toMatcherState(addr);
+    state->matcher()->useTransparentBounds(value);
+}
+
+static jint NativeMatcher_getMatchedGroupIndexImpl(JNIEnv* env, jclass, jlong patternAddr, jstring javaGroupName) {
+  icu::RegexPattern* pattern = reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(patternAddr));
+  ScopedJavaUnicodeString groupName(env, javaGroupName);
+  UErrorCode status = U_ZERO_ERROR;
+
+  jint result = pattern->groupNumberFromName(groupName.unicodeString(), status);
+  if (U_SUCCESS(status)) {
+    return result;
+  }
+  if (status == U_REGEX_INVALID_CAPTURE_GROUP_NAME) {
+    return -1;
+  }
+  maybeThrowIcuException(env, "RegexPattern::groupNumberFromName", status);
+  return -1;
+}
+
+
+static JNINativeMethod gMethods[] = {
+    NATIVE_METHOD(NativeMatcher, getMatchedGroupIndexImpl, "(JLjava/lang/String;)I"),
+    NATIVE_METHOD(NativeMatcher, findImpl, "(JI[I)Z"),
+    NATIVE_METHOD(NativeMatcher, findNextImpl, "(J[I)Z"),
+    NATIVE_METHOD(NativeMatcher, getNativeFinalizer, "()J"),
+    NATIVE_METHOD(NativeMatcher, groupCountImpl, "(J)I"),
+    NATIVE_METHOD(NativeMatcher, hitEndImpl, "(J)Z"),
+    NATIVE_METHOD(NativeMatcher, lookingAtImpl, "(J[I)Z"),
+    NATIVE_METHOD(NativeMatcher, matchesImpl, "(J[I)Z"),
+    NATIVE_METHOD(NativeMatcher, openImpl, "(J)J"),
+    NATIVE_METHOD(NativeMatcher, requireEndImpl, "(J)Z"),
+    NATIVE_METHOD(NativeMatcher, setInputImpl, "(JLjava/lang/String;II)V"),
+    NATIVE_METHOD(NativeMatcher, useAnchoringBoundsImpl, "(JZ)V"),
+    NATIVE_METHOD(NativeMatcher, useTransparentBoundsImpl, "(JZ)V"),
+};
+void register_com_android_icu_util_regex_NativeMatcher(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "com/android/icu/util/regex/NativeMatcher", gMethods, NELEM(gMethods));
+}
diff --git a/android_icu4j/src/main/jni/com_android_icu_util_regex_NativePattern.cpp b/android_icu4j/src/main/jni/com_android_icu_util_regex_NativePattern.cpp
new file mode 100644
index 0000000..f93049c
--- /dev/null
+++ b/android_icu4j/src/main/jni/com_android_icu_util_regex_NativePattern.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define LOG_TAG "NativePattern"
+
+#include <stdlib.h>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/jni_macros.h>
+
+#include "unicode/parseerr.h"
+#include "unicode/regex.h"
+
+#include "JniConstants.h"
+#include "ScopedJavaUnicodeString.h"
+
+// ICU documentation: http://icu-project.org/apiref/icu4c/classRegexPattern.html
+
+static const char* regexDetailMessage(UErrorCode status) {
+    // These human-readable error messages were culled from "utypes.h", and then slightly tuned
+    // to make more sense in context.
+    // If we don't have a special-case, we'll just return the textual name of
+    // the enum value (such as U_REGEX_RULE_SYNTAX), which is better than nothing.
+    switch (status) {
+    case U_REGEX_INTERNAL_ERROR: return "An internal error was detected";
+    case U_REGEX_RULE_SYNTAX: return "Syntax error in regexp pattern";
+    case U_REGEX_INVALID_STATE: return "Matcher in invalid state for requested operation";
+    case U_REGEX_BAD_ESCAPE_SEQUENCE: return "Unrecognized backslash escape sequence in pattern";
+    case U_REGEX_PROPERTY_SYNTAX: return "Incorrect Unicode property";
+    case U_REGEX_UNIMPLEMENTED: return "Use of unimplemented feature";
+    case U_REGEX_MISMATCHED_PAREN: return "Incorrectly nested parentheses in regexp pattern";
+    case U_REGEX_NUMBER_TOO_BIG: return "Decimal number is too large";
+    case U_REGEX_BAD_INTERVAL: return "Error in {min,max} interval";
+    case U_REGEX_MAX_LT_MIN: return "In {min,max}, max is less than min";
+    case U_REGEX_INVALID_BACK_REF: return "Back-reference to a non-existent capture group";
+    case U_REGEX_INVALID_FLAG: return "Invalid value for match mode flags";
+    case U_REGEX_LOOK_BEHIND_LIMIT: return "Look-behind pattern matches must have a bounded maximum length";
+    case U_REGEX_SET_CONTAINS_STRING: return "Regular expressions cannot have UnicodeSets containing strings";
+    case U_REGEX_OCTAL_TOO_BIG: return "Octal character constants must be <= 0377.";
+    case U_REGEX_MISSING_CLOSE_BRACKET: return "Missing closing bracket in character class";
+    case U_REGEX_INVALID_RANGE: return "In a character range [x-y], x is greater than y";
+    case U_REGEX_STACK_OVERFLOW: return "Regular expression backtrack stack overflow";
+    case U_REGEX_TIME_OUT: return "Maximum allowed match time exceeded";
+    case U_REGEX_STOPPED_BY_CALLER: return "Matching operation aborted by user callback function";
+    default:
+        return u_errorName(status);
+    }
+}
+
+static void throwPatternSyntaxException(JNIEnv* env, UErrorCode status, jstring pattern, UParseError error) {
+    static jmethodID method = env->GetMethodID(JniConstants::GetPatternSyntaxExceptionClass(env),
+            "<init>", "(Ljava/lang/String;Ljava/lang/String;I)V");
+    jstring message = env->NewStringUTF(regexDetailMessage(status));
+    jclass exceptionClass = JniConstants::GetPatternSyntaxExceptionClass(env);
+    jobject exception = env->NewObject(exceptionClass, method, message, pattern, error.offset);
+    env->Throw(reinterpret_cast<jthrowable>(exception));
+}
+
+static void NativePattern_free(void* addr) {
+    delete reinterpret_cast<icu::RegexPattern*>(addr);
+}
+
+static jlong NativePattern_getNativeFinalizer(JNIEnv*, jclass) {
+    return reinterpret_cast<jlong>(&NativePattern_free);
+}
+
+static jlong NativePattern_compileImpl(JNIEnv* env, jclass, jstring javaRegex, jint flags) {
+    flags |= UREGEX_ERROR_ON_UNKNOWN_ESCAPES;
+
+    UErrorCode status = U_ZERO_ERROR;
+    UParseError error;
+    error.offset = -1;
+
+    ScopedJavaUnicodeString regex(env, javaRegex);
+    if (!regex.valid()) {
+        return 0;
+    }
+    icu::UnicodeString& regexString(regex.unicodeString());
+    icu::RegexPattern* result = icu::RegexPattern::compile(regexString, flags, error, status);
+    if (!U_SUCCESS(status)) {
+        throwPatternSyntaxException(env, status, javaRegex, error);
+    }
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(result));
+}
+
+
+static JNINativeMethod gMethods[] = {
+    NATIVE_METHOD(NativePattern, compileImpl, "(Ljava/lang/String;I)J"),
+    NATIVE_METHOD(NativePattern, getNativeFinalizer, "()J"),
+};
+
+void register_com_android_icu_util_regex_NativePattern(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "com/android/icu/util/regex/NativePattern", gMethods, NELEM(gMethods));
+}
diff --git a/tools/srcgen/generate_android_icu4j.sh b/tools/srcgen/generate_android_icu4j.sh
index bbef01c..3335e27 100755
--- a/tools/srcgen/generate_android_icu4j.sh
+++ b/tools/srcgen/generate_android_icu4j.sh
@@ -50,8 +50,9 @@
 
 # Clean out previous generated code / resources.
 DEST_SRC_DIR=${ANDROID_ICU4J_DIR}/src/main/java
-rm -rf ${DEST_SRC_DIR}
-mkdir -p ${DEST_SRC_DIR}
+DEST_GENERATED_SRC_DIR=${DEST_SRC_DIR}/android/icu
+rm -rf ${DEST_GENERATED_SRC_DIR}
+mkdir -p ${DEST_GENERATED_SRC_DIR}
 
 DEST_RESOURCE_DIR=${ANDROID_ICU4J_DIR}/resources
 rm -rf ${DEST_RESOURCE_DIR}