Support i18n apex in Microdroid environment

The i18n APEX expects the ANDROID_I18N_ROOT and ANDROID_TZDATA_ROOT
variables to be configured.

The ANDROID_I18N_ROOT should be set to /apex/com.android.i18n, since
this is where i18n libs are located. It is no-op if i18n APEX is not
mounted.

For now the ANDROID_TZDATA_ROOT is set to a non-existent path, since
libicu seem to work correctly without it. We might need to also add
tzdata APEX to support APIS that depend on tzdata.

Bug: 390557313
Test: atest MicrodroidTests
Change-Id: Id89bd26a9bcb7b6d0aa0935171be4a4bdf916a20
diff --git a/build/microdroid/init.rc b/build/microdroid/init.rc
index 672f47d..ce26a35 100644
--- a/build/microdroid/init.rc
+++ b/build/microdroid/init.rc
@@ -35,6 +35,16 @@
     # (In Android this happens inside apexd-bootstrap.)
     wait_for_prop ro.cold_boot_done true
 
+    # We need to define ANDROID_TZDATA_ROOT otherwise libicu complains.
+    # For now set it to a non-existent value, because libicu APIs that don't depend on tzdata work
+    # correctly without it.
+    # Once loading tzdata APEX is supported, we can set this value to /apex/com.android.tzdata.
+    # This is no-op for most microdroid VMs, and is only used if com.android.i18n APEX was mounted.
+    export ANDROID_TZDATA_ROOT /does/not/exist
+    # Set ANDROID_I18N_ROOT otherwise libicu will complain.
+    # This is no-op for most microdroid VMs, and is only used if com.android.i18n APEX was mounted.
+    export ANDROID_I18N_ROOT /apex/com.android.i18n
+
 on init
     mkdir /mnt/apk 0755 root root
     mkdir /mnt/extra-apk 0755 root root
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 99300e2..284d706 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -47,6 +47,7 @@
     ],
     jni_libs: [
         "MicrodroidTestNativeLib",
+        "MicrodroidTestNativeLibWithLibIcu",
         "MicrodroidIdleNativeLib",
         "MicrodroidEmptyNativeLib",
         "MicrodroidExitNativeLib",
@@ -188,6 +189,17 @@
 }
 
 cc_library_shared {
+    name: "MicrodroidTestNativeLibWithLibIcu",
+    defaults: ["MicrodroidTestNativeLibDefaults"],
+    shared_libs: [
+        "libicu",
+    ],
+    cflags: [
+        "-D__MICRODROID_TEST_PAYLOAD_USES_LIBICU__",
+    ],
+}
+
+cc_library_shared {
     name: "MicrodroidTestNativeLibSub",
     defaults: ["avf_build_flags_cc"],
     srcs: ["src/native/testlib.cpp"],
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index e2379d7..58fa262 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -2760,7 +2760,7 @@
         grantPermission(USE_RELAXED_MICRODROID_ROLLBACK_PROTECTION_PERMISSION);
 
         VirtualMachineConfig config =
-                newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+                newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLibWithLibIcu.so")
                         .setDebugLevel(DEBUG_LEVEL_FULL)
                         .build();
 
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index c3904b7..355cfb1 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -30,6 +30,9 @@
 #include <stdio.h>
 #include <sys/capability.h>
 #include <sys/system_properties.h>
+#ifdef __MICRODROID_TEST_PAYLOAD_USES_LIBICU__
+#include <unicode/uchar.h>
+#endif
 #include <unistd.h>
 #include <vm_main.h>
 #include <vm_payload_restricted.h>
@@ -385,10 +388,13 @@
         }
 
         ScopedAStatus checkLibIcuIsAccessible() override {
+#ifdef __MICRODROID_TEST_PAYLOAD_USES_LIBICU__
             static constexpr const char* kLibIcuPath = "/apex/com.android.i18n/lib64/libicu.so";
             if (access(kLibIcuPath, R_OK) == 0) {
-                // TODO(ioffe): call an API provided by libicu.so and check that it returns expected
-                // value.
+                if (!u_hasBinaryProperty(U'❤' /* Emoji heart U+2764 */, UCHAR_EMOJI)) {
+                    return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+                                                                       "libicu broken!");
+                }
                 return ScopedAStatus::ok();
             } else {
                 std::string msg = "failed to access " + std::string(kLibIcuPath) + "(" +
@@ -396,6 +402,12 @@
                 return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
                                                                    msg.c_str());
             }
+#else
+            return ScopedAStatus::
+                    fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+                                                 "should be only used together with "
+                                                 "MicrodroidTestNativeLibWithLibIcu.so payload");
+#endif
         }
 
         ScopedAStatus quit() override { exit(0); }