Remove property android.icu.impl.ICUBinary.dataPath for apps targeting S+

This CL alone does not remove the property yet, because it provides
the replacement to set ICU4J data directories. The prebuilts and
libcore need to be updated to remove the property.

Pros:
1. The data directories are currently visible to app, but it's not
   intended, because the data files and file format are the internal
   implementation details.
2. having a recursive dependency between the ART module and the I18N
   module can be avoided, and the relationship simplified, by no longer
   relying on system properties to initialize ICU4J. Otherwise,
   ART modules needs to ask the I18N module where the files are,
   so it can set a system property, which tells the I18N module how
   to initialize.
   Instead, lazily generate the list of data directories when
   ICUBinary class is initialized.
3. Can remove the @IntraCoreApi TimeZoneDataFiles.generateIcuDataPath()
   in the CL after the ART prebuilts is updated.

Cons:
1. Maintain a large patch in ICU4J.

Note:
- TimeZoneDataFiles is removed from timezone-host target because
  it's not used, and avoid new dependency on AndroidDataFiles.

Bug: 171979766
Bug: 139480281
Test: ant core (using the upstream icu4j build system)
Test: ./updatecldrdata.py
Test: m droid cts
Test: CtsIcuTestCases
Change-Id: Ic257a654595181642c6c96189b0d94a4a0954156
diff --git a/android_icu4j/Android.bp b/android_icu4j/Android.bp
index 6749993..2c5592a 100644
--- a/android_icu4j/Android.bp
+++ b/android_icu4j/Android.bp
@@ -50,12 +50,22 @@
     path: "libcore_bridge/src/java",
 }
 
+java_defaults {
+    name: "libcore_icu_bridge_defaults",
+    srcs: [
+        "libcore_bridge/src/java/**/*.java",
+        ":app-compat-annotations-source",
+    ],
+    plugins: [
+        "compat-changeid-annotation-processor",
+    ],
+}
+
 // timezone-related source that is also used in host tests / tools and its
 // dependencies.
 filegroup {
     name: "timezone_host_files",
     srcs: [
-        "libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java",
         "libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java",
     ],
     path: "libcore_bridge/src/java",
@@ -94,6 +104,7 @@
 // not in ART module.
 java_library {
     name: "core-icu4j",
+    defaults: ["libcore_icu_bridge_defaults"],
     visibility: [
         "//packages/modules/RuntimeI18n/apex",
         "//external/robolectric-shadows",
@@ -111,7 +122,6 @@
     installable: true,
     hostdex: false,
 
-    srcs: [":libcore_icu_bridge_src_files"],
     static_libs: ["core-repackaged-icu4j"],
 
     // It is important that core-icu4j is restricted to only use stable APIs from the ART module
@@ -122,6 +132,11 @@
     dxflags: ["--core-library"],
 }
 
+platform_compat_config {
+    name: "icu4j-platform-compat-config",
+    src: ":core-icu4j",
+}
+
 java_sdk_library {
     name: "i18n.module.public.api",
     visibility: [
@@ -172,9 +187,9 @@
 // API.
 java_sdk_library {
     name: "i18n.module.intra.core.api",
+    defaults: ["libcore_icu_bridge_defaults"],
     srcs: [
         ":android_icu4j_repackaged_src_files",
-        ":libcore_icu_bridge_src_files",
     ],
     visibility: [
         "//libcore:__subpackages__",
@@ -241,9 +256,9 @@
 
 java_sdk_library {
     name: "legacy.i18n.module.platform.api",
+    defaults: ["libcore_icu_bridge_defaults"],
     srcs: [
         ":android_icu4j_repackaged_src_files",
-        ":libcore_icu_bridge_src_files",
     ],
     visibility: [
         "//libcore:__subpackages__",
@@ -269,9 +284,9 @@
 
 java_sdk_library {
     name: "stable.i18n.module.platform.api",
+    defaults: ["libcore_icu_bridge_defaults"],
     srcs: [
         ":android_icu4j_repackaged_src_files",
-        ":libcore_icu_bridge_src_files",
     ],
     visibility: [
         "//libcore:__subpackages__",
diff --git a/android_icu4j/api/intra/current.txt b/android_icu4j/api/intra/current.txt
index 46804f9..572dbee 100644
--- a/android_icu4j/api/intra/current.txt
+++ b/android_icu4j/api/intra/current.txt
@@ -88,6 +88,10 @@
 
 package com.android.i18n.system {
 
+  public class AppSpecializationHooks {
+    method public static void handleCompatChangesBeforeBindingApplication();
+  }
+
   public final class ZygoteHooks {
     method public static void onBeginPreload();
     method public static void onEndPreload();
diff --git a/android_icu4j/api/legacy_platform/current.txt b/android_icu4j/api/legacy_platform/current.txt
index 9fb6087..392b02d 100644
--- a/android_icu4j/api/legacy_platform/current.txt
+++ b/android_icu4j/api/legacy_platform/current.txt
@@ -125,6 +125,7 @@
     method public static String getDataTimeZoneFile(String);
     method public static String getDataTimeZoneRootDir();
     method public static String getTimeZoneModuleTzVersionFile();
+    method public static com.android.i18n.timezone.TzDataSetVersion readTimeZoneModuleVersion() throws java.io.IOException, com.android.i18n.timezone.TzDataSetVersion.TzDataSetException;
   }
 
   public final class TimeZoneFinder {
@@ -146,7 +147,6 @@
     method public String getRulesVersion();
     method public static boolean isCompatibleWithThisDevice(com.android.i18n.timezone.TzDataSetVersion);
     method public static com.android.i18n.timezone.TzDataSetVersion readFromFile(java.io.File) throws java.io.IOException, com.android.i18n.timezone.TzDataSetVersion.TzDataSetException;
-    method public static com.android.i18n.timezone.TzDataSetVersion readTimeZoneModuleVersion() throws java.io.IOException, com.android.i18n.timezone.TzDataSetVersion.TzDataSetException;
     method public byte[] toBytes();
     field public static final String DEFAULT_FILE_NAME = "tz_version";
   }
diff --git a/android_icu4j/libcore_bridge/src/java/com/android/i18n/system/AppSpecializationHooks.java b/android_icu4j/libcore_bridge/src/java/com/android/i18n/system/AppSpecializationHooks.java
new file mode 100644
index 0000000..644f341
--- /dev/null
+++ b/android_icu4j/libcore_bridge/src/java/com/android/i18n/system/AppSpecializationHooks.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.i18n.system;
+
+import libcore.api.IntraCoreApi;
+
+/**
+ * Provides hooks for {@link dalvik.system.AppSpecializationHooks} to call into this class to
+ * specialize ICU in an app process.
+ *
+ * @hide
+ */
+@IntraCoreApi
+public class AppSpecializationHooks {
+
+    /**
+     * Called by
+     * {@link dalvik.system.AppSpecializationHooks#handleCompatChangesBeforeBindingApplication()}.
+     */
+    @IntraCoreApi
+    public static void handleCompatChangesBeforeBindingApplication() {
+        ZygoteHooks.handleCompatChangesBeforeBindingApplication();
+    }
+}
diff --git a/android_icu4j/libcore_bridge/src/java/com/android/i18n/system/ZygoteHooks.java b/android_icu4j/libcore_bridge/src/java/com/android/i18n/system/ZygoteHooks.java
index 26e926e..8e88c1d 100644
--- a/android_icu4j/libcore_bridge/src/java/com/android/i18n/system/ZygoteHooks.java
+++ b/android_icu4j/libcore_bridge/src/java/com/android/i18n/system/ZygoteHooks.java
@@ -16,12 +16,18 @@
 
 package com.android.i18n.system;
 
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.icu.platform.AndroidDataFiles;
 import android.icu.impl.CacheValue;
 import android.icu.text.DateFormatSymbols;
 import android.icu.text.DecimalFormatSymbols;
 import android.icu.util.TimeZone;
 import android.icu.util.ULocale;
 
+import dalvik.annotation.compat.VersionCodes;
+
 /**
  * Provides hooks for {@link dalvik.system.ZygoteHooks} to call into this class to initialize
  * in the Zygote.
@@ -66,5 +72,31 @@
     public static void onEndPreload() {
         // All cache references created by ICU from this point will be soft.
         CacheValue.setStrength(CacheValue.Strength.SOFT);
+
+        // The PROP_ICUBINARY_DATA_PATH property was used on Android before S to configure its copy
+        // of ICU4J before the ART module was split out. ICU4J on Android now carries a patch so the
+        // system property is not needed. It is still set as a System property and it is cleared in
+        // disableCompatChangesBeforeAppStart() after forking, if android.compat.Compatibility
+        // allow.
+        // It's in the end of preload because preload and ICU4J initialization should succeed
+        // without this property. Otherwise, it indicates that the Android patch is not working.
+        System.setProperty(PROP_ICUBINARY_DATA_PATH, AndroidDataFiles.generateIcuDataPath());
+    }
+
+    /**
+     * Remove "android.icu.impl.ICUBinary.dataPath" property for apps targeting S+. The ICU data
+     * path should not visible to the apps.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = VersionCodes.R)
+    private static final long HIDE_PROP_ICUBINARY_DATA_PATH = 171979766L; // This is a bug id.
+
+    // VisibleForTesting
+    public static final String PROP_ICUBINARY_DATA_PATH = "android.icu.impl.ICUBinary.dataPath";
+
+    static void handleCompatChangesBeforeBindingApplication() {
+        if (Compatibility.isChangeEnabled(HIDE_PROP_ICUBINARY_DATA_PATH)) {
+            System.clearProperty(PROP_ICUBINARY_DATA_PATH);
+        }
     }
 }
diff --git a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java
index d8a58dc..fd324cc 100644
--- a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java
+++ b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TimeZoneDataFiles.java
@@ -16,21 +16,27 @@
 
 package com.android.i18n.timezone;
 
+import android.icu.platform.AndroidDataFiles;
+
+import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * Utility methods associated with finding updateable time zone data files.
+ * Utility methods associated with finding updateable time zone data files. ICU4C and ICU4J also
+ * read files affected by time zone updates. That logic is kept in {@link AndroidDataFiles}
+ * and should be updated if file locations or lookup order changes.
  *
  * @hide
  */
 @libcore.api.CorePlatformApi
 @libcore.api.IntraCoreApi
 public final class TimeZoneDataFiles {
-    private static final String ANDROID_ROOT_ENV = "ANDROID_ROOT";
-    private static final String ANDROID_I18N_ROOT_ENV = "ANDROID_I18N_ROOT";
-    private static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT";
-    private static final String ANDROID_DATA_ENV = "ANDROID_DATA";
+    private static final String ANDROID_ROOT_ENV = AndroidDataFiles.ANDROID_ROOT_ENV;
+    private static final String ANDROID_I18N_ROOT_ENV = AndroidDataFiles.ANDROID_I18N_ROOT_ENV;
+    private static final String ANDROID_TZDATA_ROOT_ENV = AndroidDataFiles.ANDROID_TZDATA_ROOT_ENV;
+    private static final String ANDROID_DATA_ENV = AndroidDataFiles.ANDROID_DATA_ENV;
 
     private TimeZoneDataFiles() {}
 
@@ -76,6 +82,16 @@
         return getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
     }
 
+    /**
+     * Reads the version of time zone data supplied by the time zone data module.
+     */
+    @libcore.api.CorePlatformApi
+    public static TzDataSetVersion readTimeZoneModuleVersion()
+            throws IOException, TzDataSetVersion.TzDataSetException {
+        String tzVersionFileName = getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
+        return TzDataSetVersion.readFromFile(new File(tzVersionFileName));
+    }
+
     // VisibleForTesting
     public static String getTimeZoneModuleFile(String fileName) {
         return System.getenv(ANDROID_TZDATA_ROOT_ENV) + "/etc/" + fileName;
@@ -93,10 +109,6 @@
         return getEnvironmentPath(ANDROID_ROOT_ENV, "/usr/share/zoneinfo/" + fileName);
     }
 
-    public static String getSystemIcuFile(String fileName) {
-        return getEnvironmentPath(ANDROID_ROOT_ENV, "/usr/icu/" + fileName);
-    }
-
     @libcore.api.IntraCoreApi
     public static String generateIcuDataPath() {
         List<String> paths = new ArrayList<>(3);
@@ -125,6 +137,11 @@
         return String.join(":", paths);
     }
 
+    // VisibleForTesting
+    public static String getSystemIcuFile(String fileName) {
+        return getEnvironmentPath(ANDROID_ROOT_ENV, "/usr/icu/" + fileName);
+    }
+
     /**
      * Creates a path by combining the value of an environment variable with a relative path.
      * Returns {@code null} if the environment variable is not set.
diff --git a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java
index 7ce39e5..ac73b37 100644
--- a/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java
+++ b/android_icu4j/libcore_bridge/src/java/com/android/i18n/timezone/TzDataSetVersion.java
@@ -167,17 +167,6 @@
         return fromBytes(versionBytes);
     }
 
-    /**
-     * Reads the version of time zone data supplied by the time zone data module.
-     */
-    @libcore.api.CorePlatformApi
-    public static TzDataSetVersion readTimeZoneModuleVersion()
-            throws IOException, TzDataSetException {
-        String tzVersionFileName =
-                TimeZoneDataFiles.getTimeZoneModuleTzFile(TzDataSetVersion.DEFAULT_FILE_NAME);
-        return readFromFile(new File(tzVersionFileName));
-    }
-
     /** Returns the major version number. See {@link TzDataSetVersion}. */
     @libcore.api.CorePlatformApi
     public int getFormatMajorVersion() {
diff --git a/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java b/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java
index a35ff3b..c315559 100644
--- a/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java
+++ b/android_icu4j/src/main/java/android/icu/impl/ICUBinary.java
@@ -25,6 +25,7 @@
 import java.util.MissingResourceException;
 import java.util.Set;
 
+import android.icu.platform.AndroidDataFiles;
 import android.icu.util.ICUUncheckedIOException;
 import android.icu.util.VersionInfo;
 
@@ -285,8 +286,17 @@
     private static final List<DataFile> icuDataFiles = new ArrayList<>();
 
     static {
+        // BEGIN Android-changed: Initialize ICU data file paths.
+        /*
         // Normally android.icu.impl.ICUBinary.dataPath.
         String dataPath = ICUConfig.get(ICUBinary.class.getName() + ".dataPath");
+        */
+        String dataPath = null;
+        // Only when runs after repackaging ICU4J. Otherwise the jar should have the ICU resources.
+        if (ICUBinary.class.getName().startsWith("android.icu")) {
+            dataPath = AndroidDataFiles.generateIcuDataPath();
+        }
+        // END Android-changed: Initialize ICU data file paths.
         if (dataPath != null) {
             addDataFilesFromPath(dataPath, icuDataFiles);
         }
diff --git a/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java b/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java
new file mode 100644
index 0000000..73e2d50
--- /dev/null
+++ b/android_icu4j/src/main/java/android/icu/platform/AndroidDataFiles.java
@@ -0,0 +1,98 @@
+/* GENERATED SOURCE. DO NOT MODIFY. */
+/*
+ * Copyright (C) 2017 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 android.icu.platform;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Android-added: Used by {@link android.icu.impl.ICUBinary} to locate ICU data files.
+ * The paths to ICU data files are generated dynamically from environment variables because
+ * the paths are different in various test environments. Thus, the paths can't be generated into
+ * ICUConfig.properties in the compile-time, and generated by this class instead.
+ *
+ * {@link com.android.i18n.timezone.TimeZoneDataFiles} holds the information related to other
+ * timezone data files.
+ * @hide Only a subset of ICU is exposed in Android
+ */
+public class AndroidDataFiles {
+    /**
+     * The below are public to be used by {@link com.android.i18n.timezone.TimeZoneDataFiles}.
+     */
+    public static final String ANDROID_ROOT_ENV = "ANDROID_ROOT";
+    public static final String ANDROID_I18N_ROOT_ENV = "ANDROID_I18N_ROOT";
+    public static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT";
+    public static final String ANDROID_DATA_ENV = "ANDROID_DATA";
+
+    // VisibleForTesting
+    public static String getTimeZoneModuleIcuFile(String fileName) {
+        return getTimeZoneModuleFile("icu/" + fileName);
+    }
+
+    private static String getTimeZoneModuleFile(String fileName) {
+        return System.getenv(ANDROID_TZDATA_ROOT_ENV) + "/etc/" + fileName;
+    }
+
+    // VisibleForTesting
+    public static String getI18nModuleIcuFile(String fileName) {
+        return getI18nModuleFile("icu/" + fileName);
+    }
+
+    private static String getI18nModuleFile(String fileName) {
+        return System.getenv(ANDROID_I18N_ROOT_ENV) + "/etc/" + fileName;
+    }
+
+    public static String generateIcuDataPath() {
+        List<String> paths = new ArrayList<>(3);
+
+        // Note: This logic below should match the logic in IcuRegistration.cpp in external/icu/
+        // to ensure consistent behavior between ICU4C and ICU4J.
+
+        // ICU should first look in ANDROID_DATA. This is used for (optional) time zone data
+        // delivered by APK (https://source.android.com/devices/tech/config/timezone-rules)
+        String dataIcuDataPath =
+                getEnvironmentPath(ANDROID_DATA_ENV, "/misc/zoneinfo/current/icu/");
+        if (dataIcuDataPath != null) {
+            paths.add(dataIcuDataPath);
+        }
+
+        // ICU should then look for a mounted time zone module file in /apex. This is used for
+        // (optional) time zone data that can be updated with an APEX file.
+        String timeZoneModuleIcuDataPath = getTimeZoneModuleIcuFile("");
+        paths.add(timeZoneModuleIcuDataPath);
+
+        // ICU should always look in the i18n module path as this is where most of the data
+        // can be found.
+        String i18nModuleIcuDataPath = getI18nModuleIcuFile("");
+        paths.add(i18nModuleIcuDataPath);
+
+        return String.join(":", paths);
+    }
+
+    /**
+     * Creates a path by combining the value of an environment variable with a relative path.
+     * Returns {@code null} if the environment variable is not set.
+     */
+    private static String getEnvironmentPath(String environmentVariable, String path) {
+        String variable = System.getenv(environmentVariable);
+        if (variable == null) {
+            return null;
+        }
+        return variable + path;
+    }
+}
diff --git a/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java b/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java
index 13884bf..9c567eb 100644
--- a/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java
+++ b/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneDataFilesTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.icu.platform.AndroidDataFiles;
 import android.icu.testsharding.MainTestShard;
 import com.android.i18n.timezone.TimeZoneDataFiles;
 import org.junit.Test;
@@ -55,8 +56,7 @@
     // http://b/34867424
     @Test
     public void generateIcuDataPath_includesTimeZoneOverride() {
-        String icuDataPath = System.getProperty("android.icu.impl.ICUBinary.dataPath");
-        assertEquals(icuDataPath, TimeZoneDataFiles.generateIcuDataPath());
+        String icuDataPath = AndroidDataFiles.generateIcuDataPath();
 
         String[] paths = icuDataPath.split(":");
         assertEquals(3, paths.length);
diff --git a/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneIntegrationTest.java b/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneIntegrationTest.java
index 8749ac8..22bde5f 100644
--- a/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneIntegrationTest.java
+++ b/android_icu4j/testing/src/com/android/i18n/test/timezone/TimeZoneIntegrationTest.java
@@ -24,6 +24,7 @@
 import com.android.i18n.timezone.ZoneInfoDb;
 import org.junit.Test;
 
+import android.icu.platform.AndroidDataFiles;
 import android.icu.text.TimeZoneNames;
 import android.icu.util.VersionInfo;
 import android.system.Os;
@@ -52,6 +53,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+
 /**
  * Tests that compare ICU and libcore time zone behavior and similar cross-cutting concerns.
  */
@@ -272,7 +274,7 @@
         String apexRootDir = TimeZoneDataFiles.getTimeZoneModuleFile("");
         List<String> dataModuleFiles =
                 createModuleTzFiles(TimeZoneDataFiles::getTimeZoneModuleTzFile);
-        String icuOverlayFile = TimeZoneDataFiles.getTimeZoneModuleIcuFile("icu_tzdata.dat");
+        String icuOverlayFile = AndroidDataFiles.getTimeZoneModuleIcuFile("icu_tzdata.dat");
         if (fileExists(apexRootDir)) {
             assertEquals("OK", tzModuleStatus);
             dataModuleFiles.forEach(TimeZoneIntegrationTest::assertFileExists);
@@ -284,7 +286,7 @@
         }
 
         String icuDatFileName = "icudt" + VersionInfo.ICU_VERSION.getMajor() + "l.dat";
-        String i18nModuleIcuData = TimeZoneDataFiles.getI18nModuleIcuFile(icuDatFileName);
+        String i18nModuleIcuData = AndroidDataFiles.getI18nModuleIcuFile(icuDatFileName);
         assertFileExists(i18nModuleIcuData);
 
         // Devices currently have a subset of the time zone files in /system. These are going away
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java
index 8a8852b..fcc5248 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/ICUBinary.java
@@ -24,6 +24,7 @@
 import java.util.MissingResourceException;
 import java.util.Set;
 
+import com.ibm.icu.platform.AndroidDataFiles;
 import com.ibm.icu.util.ICUUncheckedIOException;
 import com.ibm.icu.util.VersionInfo;
 
@@ -281,8 +282,17 @@
     private static final List<DataFile> icuDataFiles = new ArrayList<>();
 
     static {
+        // BEGIN Android-changed: Initialize ICU data file paths.
+        /*
         // Normally com.ibm.icu.impl.ICUBinary.dataPath.
         String dataPath = ICUConfig.get(ICUBinary.class.getName() + ".dataPath");
+        */
+        String dataPath = null;
+        // Only when runs after repackaging ICU4J. Otherwise the jar should have the ICU resources.
+        if (ICUBinary.class.getName().startsWith("android.icu")) {
+            dataPath = AndroidDataFiles.generateIcuDataPath();
+        }
+        // END Android-changed: Initialize ICU data file paths.
         if (dataPath != null) {
             addDataFilesFromPath(dataPath, icuDataFiles);
         }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/platform/AndroidDataFiles.java b/icu4j/main/classes/core/src/com/ibm/icu/platform/AndroidDataFiles.java
new file mode 100644
index 0000000..e599957
--- /dev/null
+++ b/icu4j/main/classes/core/src/com/ibm/icu/platform/AndroidDataFiles.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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.ibm.icu.platform;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Android-added: Used by {@link com.ibm.icu.impl.ICUBinary} to locate ICU data files.
+ * The paths to ICU data files are generated dynamically from environment variables because
+ * the paths are different in various test environments. Thus, the paths can't be generated into
+ * ICUConfig.properties in the compile-time, and generated by this class instead.
+ *
+ * {@link com.android.i18n.timezone.TimeZoneDataFiles} holds the information related to other
+ * timezone data files.
+ */
+public class AndroidDataFiles {
+    /**
+     * The below are public to be used by {@link com.android.i18n.timezone.TimeZoneDataFiles}.
+     */
+    public static final String ANDROID_ROOT_ENV = "ANDROID_ROOT";
+    public static final String ANDROID_I18N_ROOT_ENV = "ANDROID_I18N_ROOT";
+    public static final String ANDROID_TZDATA_ROOT_ENV = "ANDROID_TZDATA_ROOT";
+    public static final String ANDROID_DATA_ENV = "ANDROID_DATA";
+
+    // VisibleForTesting
+    public static String getTimeZoneModuleIcuFile(String fileName) {
+        return getTimeZoneModuleFile("icu/" + fileName);
+    }
+
+    private static String getTimeZoneModuleFile(String fileName) {
+        return System.getenv(ANDROID_TZDATA_ROOT_ENV) + "/etc/" + fileName;
+    }
+
+    // VisibleForTesting
+    public static String getI18nModuleIcuFile(String fileName) {
+        return getI18nModuleFile("icu/" + fileName);
+    }
+
+    private static String getI18nModuleFile(String fileName) {
+        return System.getenv(ANDROID_I18N_ROOT_ENV) + "/etc/" + fileName;
+    }
+
+    public static String generateIcuDataPath() {
+        List<String> paths = new ArrayList<>(3);
+
+        // Note: This logic below should match the logic in IcuRegistration.cpp in external/icu/
+        // to ensure consistent behavior between ICU4C and ICU4J.
+
+        // ICU should first look in ANDROID_DATA. This is used for (optional) time zone data
+        // delivered by APK (https://source.android.com/devices/tech/config/timezone-rules)
+        String dataIcuDataPath =
+                getEnvironmentPath(ANDROID_DATA_ENV, "/misc/zoneinfo/current/icu/");
+        if (dataIcuDataPath != null) {
+            paths.add(dataIcuDataPath);
+        }
+
+        // ICU should then look for a mounted time zone module file in /apex. This is used for
+        // (optional) time zone data that can be updated with an APEX file.
+        String timeZoneModuleIcuDataPath = getTimeZoneModuleIcuFile("");
+        paths.add(timeZoneModuleIcuDataPath);
+
+        // ICU should always look in the i18n module path as this is where most of the data
+        // can be found.
+        String i18nModuleIcuDataPath = getI18nModuleIcuFile("");
+        paths.add(i18nModuleIcuDataPath);
+
+        return String.join(":", paths);
+    }
+
+    /**
+     * Creates a path by combining the value of an environment variable with a relative path.
+     * Returns {@code null} if the environment variable is not set.
+     */
+    private static String getEnvironmentPath(String environmentVariable, String path) {
+        String variable = System.getenv(environmentVariable);
+        if (variable == null) {
+            return null;
+        }
+        return variable + path;
+    }
+}