Do the keyword replacement when parsing the vm config file

... and move the replacement routine from InstallUtils to ConfigJson
as the replacement is specific to the config file.

Bug: N/A
Test: N/A
Change-Id: I435191e0f824ade4133521c9e9ff2f54879eb298
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java b/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java
index 70ab2e0..cec1b7a 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java
@@ -35,9 +35,16 @@
 import com.google.gson.Gson;
 import com.google.gson.annotations.SerializedName;
 
+import java.io.BufferedReader;
 import java.io.FileReader;
+import java.io.IOException;
+import java.io.PipedReader;
+import java.io.PipedWriter;
+import java.io.Reader;
 import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Objects;
 
 /** This class and its inner classes model vm_config.json. */
@@ -70,14 +77,54 @@
     private GpuJson gpu;
 
     /** Parses JSON file at jsonPath */
-    static ConfigJson from(Path jsonPath) {
-        try (FileReader r = new FileReader(jsonPath.toFile())) {
+    static ConfigJson from(Context context, Path jsonPath) {
+        try (FileReader fileReader = new FileReader(jsonPath.toFile());
+                Reader r = replaceKeywords(fileReader, context)) {
             return new Gson().fromJson(r, ConfigJson.class);
         } catch (Exception e) {
             throw new RuntimeException("Failed to parse " + jsonPath, e);
         }
     }
 
+    private static Reader replaceKeywords(Reader r, Context context) throws IOException {
+        PipedWriter pipeIn = new PipedWriter();
+        PipedReader pipeOut = new PipedReader();
+        pipeOut.connect(pipeIn);
+
+        Map<String, String> rules = new HashMap<>();
+        rules.put("\\$PAYLOAD_DIR", InstallUtils.getInternalStorageDir(context).toString());
+        rules.put("\\$USER_ID", String.valueOf(context.getUserId()));
+        rules.put("\\$PACKAGE_NAME", context.getPackageName());
+        String appDataDir = context.getDataDir().toString();
+        // TODO: remove this hack
+        if (context.getUserId() == 0) {
+            appDataDir = "/data/data/" + context.getPackageName();
+        }
+        rules.put("\\$APP_DATA_DIR", appDataDir);
+
+        try (BufferedReader br = new BufferedReader(r)) {
+            br.lines()
+                    .map(
+                            line -> {
+                                for (Map.Entry<String, String> rule : rules.entrySet()) {
+                                    line = line.replaceAll(rule.getKey(), rule.getValue());
+                                }
+                                return line;
+                            })
+                    .forEach(
+                            line -> {
+                                try {
+                                    pipeIn.write(line);
+                                    pipeIn.write('\n');
+                                } catch (IOException e) {
+                                    // this cannot happen as it is connected to a pipe.
+                                    throw new RuntimeException(e);
+                                }
+                            });
+        }
+        return pipeOut;
+    }
+
     private int getCpuTopology() {
         switch (cpu_topology) {
             case "one_cpu":
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallUtils.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallUtils.java
index b8de66d..1b6da6c 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallUtils.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallUtils.java
@@ -35,9 +35,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Function;
 
 public class InstallUtils {
     private static final String VM_CONFIG_FILENAME = "vm_config.json";
@@ -129,15 +126,6 @@
             Log.e(TAG, "installation failed", e);
             return false;
         }
-        if (!resolvePathInVmConfig(context)) {
-            Log.d(TAG, "resolving path failed");
-            try {
-                Files.deleteIfExists(getVmConfigPath(context));
-            } catch (IOException e) {
-                return false;
-            }
-            return false;
-        }
 
         // remove payload if installation is done.
         try {
@@ -150,41 +138,6 @@
         return createInstalledMarker(context);
     }
 
-    private static Function<String, String> getReplacer(Context context) {
-        Map<String, String> rules = new HashMap<>();
-        rules.put("\\$PAYLOAD_DIR", new File(context.getFilesDir(), PAYLOAD_DIR).toString());
-        rules.put("\\$USER_ID", String.valueOf(context.getUserId()));
-        rules.put("\\$PACKAGE_NAME", context.getPackageName());
-        String appDataDir = context.getDataDir().toString();
-        // TODO: remove this hack
-        if (context.getUserId() == 0) {
-            appDataDir = "/data/data/" + context.getPackageName();
-        }
-        rules.put("\\$APP_DATA_DIR", appDataDir);
-        return (s) -> {
-            for (Map.Entry<String, String> rule : rules.entrySet()) {
-                s = s.replaceAll(rule.getKey(), rule.getValue());
-            }
-            return s;
-        };
-    }
-
-    public static boolean resolvePathInVmConfig(Context context) {
-        try {
-            Path configPath = getVmConfigPath(context);
-            String replacedVmConfig =
-                    String.join(
-                            "\n",
-                            Files.readAllLines(configPath).stream()
-                                    .map(getReplacer(context))
-                                    .toList());
-            Files.write(configPath, replacedVmConfig.getBytes());
-            return true;
-        } catch (IOException e) {
-            return false;
-        }
-    }
-
     public static Path getRootfsFile(Context context) throws FileNotFoundException {
         Path path = getInternalStorageDir(context).resolve(ROOTFS_FILENAME);
         if (!Files.exists(path)) {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
index f58c322..4b2e640 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
@@ -261,10 +261,6 @@
             return false;
         }
 
-        if (!InstallUtils.resolvePathInVmConfig(this)) {
-            notifyError(getString(R.string.installer_error_unknown));
-            return false;
-        }
         return InstallUtils.createInstalledMarker(this);
     }
 
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java
index a593d0b..879b5e5 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.java
@@ -152,7 +152,7 @@
         }
         mExecutorService = Executors.newCachedThreadPool();
 
-        ConfigJson json = ConfigJson.from(InstallUtils.getVmConfigPath(this));
+        ConfigJson json = ConfigJson.from(this, InstallUtils.getVmConfigPath(this));
         VirtualMachineConfig.Builder configBuilder = json.toConfigBuilder(this);
         VirtualMachineCustomImageConfig.Builder customImageConfigBuilder =
                 json.toCustomImageConfigBuilder(this);