Merge "(experimental) Enable VirGL if the flag file exists" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java b/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java
index cec1b7a..5cf123e 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ConfigJson.java
@@ -38,14 +38,13 @@
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;
+import java.util.stream.Collectors;
/** This class and its inner classes model vm_config.json. */
class ConfigJson {
@@ -78,19 +77,15 @@
/** Parses JSON file at jsonPath */
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);
+ try (FileReader fileReader = new FileReader(jsonPath.toFile())) {
+ String content = replaceKeywords(fileReader, context);
+ return new Gson().fromJson(content, 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);
-
+ private static String replaceKeywords(Reader r, Context context) throws IOException {
Map<String, String> rules = new HashMap<>();
rules.put("\\$PAYLOAD_DIR", InstallUtils.getInternalStorageDir(context).toString());
rules.put("\\$USER_ID", String.valueOf(context.getUserId()));
@@ -103,7 +98,7 @@
rules.put("\\$APP_DATA_DIR", appDataDir);
try (BufferedReader br = new BufferedReader(r)) {
- br.lines()
+ return br.lines()
.map(
line -> {
for (Map.Entry<String, String> rule : rules.entrySet()) {
@@ -111,18 +106,8 @@
}
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);
- }
- });
+ .collect(Collectors.joining("\n"));
}
- return pipeOut;
}
private int getCpuTopology() {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.java
index ee1f1ad..44dcce5 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.java
@@ -16,6 +16,7 @@
package com.android.virtualization.terminal;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
@@ -25,7 +26,14 @@
import androidx.annotation.Nullable;
public class ErrorActivity extends BaseActivity {
- public static final String EXTRA_CAUSE = "cause";
+ private static final String EXTRA_CAUSE = "cause";
+
+ public static void start(Context context, Exception e) {
+ Intent intent = new Intent(context, ErrorActivity.class);
+ intent.putExtra(EXTRA_CAUSE, e);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
index c4d11e8..54aa07a 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ImageArchive.java
@@ -27,6 +27,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
@@ -80,6 +81,51 @@
}
}
+ /**
+ * Creates ImageArchive from either SdCard or Internet. SdCard is used only when the build is
+ * debuggable and the file actually exists.
+ */
+ public static ImageArchive getDefault() {
+ ImageArchive archive = fromSdCard();
+ if (Build.isDebuggable() && archive.exists()) {
+ return archive;
+ } else {
+ return fromInternet();
+ }
+ }
+
+ /** Tests if ImageArchive exists on the medium. */
+ public boolean exists() {
+ if (mPath != null) {
+ return Files.exists(mPath);
+ } else {
+ // TODO
+ return true;
+ }
+ }
+
+ /** Returns size of the archive in bytes */
+ public long getSize() throws IOException {
+ if (!exists()) {
+ throw new IllegalStateException("Cannot get size of non existing archive");
+ }
+ if (mPath != null) {
+ return Files.size(mPath);
+ } else {
+ HttpURLConnection conn = null;
+ try {
+ conn = (HttpURLConnection) mUrl.openConnection();
+ conn.setRequestMethod("HEAD");
+ conn.getInputStream();
+ return conn.getContentLength();
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ }
+ }
+
private InputStream getInputStream(Function<InputStream, InputStream> filter)
throws IOException {
InputStream is = mPath != null ? new FileInputStream(mPath.toFile()) : mUrl.openStream();
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
index 52ef3d4..69b5ee7 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerActivity.java
@@ -41,6 +41,7 @@
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.google.android.material.snackbar.Snackbar;
+import java.io.IOException;
import java.lang.ref.WeakReference;
public class InstallerActivity extends BaseActivity {
@@ -63,12 +64,8 @@
mInstallProgressListener = new InstallProgressListener(this);
setContentView(R.layout.activity_installer);
-
- TextView desc = (TextView) findViewById(R.id.installer_desc);
- desc.setText(
- getString(
- R.string.installer_desc_text_format,
- Formatter.formatShortFileSize(this, ESTIMATED_IMG_SIZE_BYTES)));
+ updateSizeEstimation(ESTIMATED_IMG_SIZE_BYTES);
+ measureImageSizeAndUpdateDescription();
mWaitForWifiCheckbox = (CheckBox) findViewById(R.id.installer_wait_for_wifi_checkbox);
mInstallButton = (TextView) findViewById(R.id.installer_install_button);
@@ -85,6 +82,33 @@
}
}
+ private void updateSizeEstimation(long est) {
+ String desc =
+ getString(
+ R.string.installer_desc_text_format,
+ Formatter.formatShortFileSize(this, est));
+ runOnUiThread(
+ () -> {
+ TextView view = (TextView) findViewById(R.id.installer_desc);
+ view.setText(desc);
+ });
+ }
+
+ private void measureImageSizeAndUpdateDescription() {
+ new Thread(
+ () -> {
+ long est;
+ try {
+ est = ImageArchive.getDefault().getSize();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to measure image size.", e);
+ return;
+ }
+ updateSizeEstimation(est);
+ })
+ .start();
+ }
+
@Override
public void onResume() {
super.onResume();
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index a3b0577..ded186f 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -197,7 +197,7 @@
public boolean dispatchKeyEvent(KeyEvent event) {
if (Build.isDebuggable() && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
if (event.getAction() == KeyEvent.ACTION_UP) {
- launchErrorActivity(new Exception("Debug: KeyEvent.KEYCODE_UNKNOWN"));
+ ErrorActivity.start(this, new Exception("Debug: KeyEvent.KEYCODE_UNKNOWN"));
}
return true;
}
@@ -444,7 +444,8 @@
@Override
public void onVmError() {
Log.i(TAG, "onVmError()");
- launchErrorActivity(new Exception("onVmError"));
+ // TODO: error cause is too simple.
+ ErrorActivity.start(this, new Exception("onVmError"));
}
@Override
@@ -501,13 +502,6 @@
}
}
- private void launchErrorActivity(Exception e) {
- Intent intent = new Intent(this, ErrorActivity.class);
- intent.putExtra(ErrorActivity.EXTRA_CAUSE, e);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
- this.startActivity(intent);
- }
-
private boolean installIfNecessary() {
// If payload from external storage exists(only for debuggable build) or there is no
// installed image, launch installer activity.