Merge "Convert Logger to kotlin" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.java
deleted file mode 100644
index 7099f22..0000000
--- a/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2024 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.virtualization.terminal;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-public class ErrorActivity extends BaseActivity {
- 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) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_error);
-
- View button = findViewById(R.id.recovery);
- button.setOnClickListener((event) -> launchRecoveryActivity());
- }
-
- @Override
- protected void onNewIntent(@NonNull Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- Intent intent = getIntent();
- Exception e = intent.getParcelableExtra(EXTRA_CAUSE, Exception.class);
- TextView cause = findViewById(R.id.cause);
- if (e != null) {
- String stackTrace = getStackTrace(e);
- cause.setText(getString(R.string.error_code, stackTrace));
- } else {
- cause.setText(null);
- }
- }
-
- private void launchRecoveryActivity() {
- Intent intent = new Intent(this, SettingsRecoveryActivity.class);
- startActivity(intent);
- }
-
- private static String getStackTrace(Exception e) {
- try (StringWriter sWriter = new StringWriter();
- PrintWriter pWriter = new PrintWriter(sWriter)) {
- e.printStackTrace(pWriter);
- return sWriter.toString();
- } catch (IOException ex) {
- // This shall never happen
- throw new RuntimeException(ex);
- }
- }
-}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.kt
new file mode 100644
index 0000000..f253f86
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/ErrorActivity.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2024 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.virtualization.terminal
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import java.io.IOException
+import java.io.PrintWriter
+import java.io.StringWriter
+import java.lang.Exception
+import java.lang.RuntimeException
+
+class ErrorActivity : BaseActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_error)
+
+ val button = findViewById<View>(R.id.recovery)
+ button.setOnClickListener(View.OnClickListener { _ -> launchRecoveryActivity() })
+ }
+
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ setIntent(intent)
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ val intent = getIntent()
+ val e = intent.getParcelableExtra<Exception?>(EXTRA_CAUSE, Exception::class.java)
+ val cause = findViewById<TextView>(R.id.cause)
+ cause.text = e?.let { getString(R.string.error_code, getStackTrace(it)) }
+ }
+
+ private fun launchRecoveryActivity() {
+ val intent = Intent(this, SettingsRecoveryActivity::class.java)
+ startActivity(intent)
+ }
+
+ companion object {
+ private const val EXTRA_CAUSE = "cause"
+
+ @JvmStatic
+ fun start(context: Context, e: Exception) {
+ val intent = Intent(context, ErrorActivity::class.java)
+ intent.putExtra(EXTRA_CAUSE, e)
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(intent)
+ }
+
+ private fun getStackTrace(e: Exception): String? {
+ try {
+ StringWriter().use { sWriter ->
+ PrintWriter(sWriter).use { pWriter ->
+ e.printStackTrace(pWriter)
+ return sWriter.toString()
+ }
+ }
+ } catch (ex: IOException) {
+ // This shall never happen
+ throw RuntimeException(ex)
+ }
+ }
+ }
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
deleted file mode 100644
index 318f49a..0000000
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2024 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.virtualization.terminal;
-
-import static com.android.virtualization.terminal.MainActivity.TAG;
-
-import android.content.Context;
-import android.os.FileUtils;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.FileDescriptor;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-
-/** Collection of files that consist of a VM image. */
-class InstalledImage {
- private static final String INSTALL_DIRNAME = "linux";
- private static final String ROOTFS_FILENAME = "root_part";
- private static final String BACKUP_FILENAME = "root_part_backup";
- private static final String CONFIG_FILENAME = "vm_config.json";
- private static final String BUILD_ID_FILENAME = "build_id";
- static final String MARKER_FILENAME = "completed";
-
- public static final long RESIZE_STEP_BYTES = 4 << 20; // 4 MiB
-
- private final Path mDir;
- private final Path mRootPartition;
- private final Path mBackup;
- private final Path mConfig;
- private final Path mMarker;
- private String mBuildId;
-
- /** Returns InstalledImage for a given app context */
- public static InstalledImage getDefault(Context context) {
- Path installDir = context.getFilesDir().toPath().resolve(INSTALL_DIRNAME);
- return new InstalledImage(installDir);
- }
-
- private InstalledImage(Path dir) {
- mDir = dir;
- mRootPartition = dir.resolve(ROOTFS_FILENAME);
- mBackup = dir.resolve(BACKUP_FILENAME);
- mConfig = dir.resolve(CONFIG_FILENAME);
- mMarker = dir.resolve(MARKER_FILENAME);
- }
-
- public Path getInstallDir() {
- return mDir;
- }
-
- /** Tests if this InstalledImage is actually installed. */
- public boolean isInstalled() {
- return Files.exists(mMarker);
- }
-
- /** Fully understalls this InstalledImage by deleting everything. */
- public void uninstallFully() throws IOException {
- FileUtils.deleteContentsAndDir(mDir.toFile());
- }
-
- /** Returns the path to the VM config file. */
- public Path getConfigPath() {
- return mConfig;
- }
-
- /** Returns the build ID of the installed image */
- public String getBuildId() {
- if (mBuildId == null) {
- mBuildId = readBuildId();
- }
- return mBuildId;
- }
-
- private String readBuildId() {
- Path file = mDir.resolve(BUILD_ID_FILENAME);
- if (!Files.exists(file)) {
- return "<no build id>";
- }
- try (BufferedReader r = new BufferedReader(new FileReader(file.toFile()))) {
- return r.readLine();
- } catch (IOException e) {
- throw new RuntimeException("Failed to read build ID", e);
- }
- }
-
- public Path uninstallAndBackup() throws IOException {
- Files.delete(mMarker);
- Files.move(mRootPartition, mBackup, StandardCopyOption.REPLACE_EXISTING);
- return mBackup;
- }
-
- public Path getBackupFile() {
- return mBackup;
- }
-
- public boolean hasBackup() {
- return Files.exists(mBackup);
- }
-
- public void deleteBackup() throws IOException {
- Files.deleteIfExists(mBackup);
- }
-
- public long getSize() throws IOException {
- return Files.size(mRootPartition);
- }
-
- public long getSmallestSizePossible() throws IOException {
- runE2fsck(mRootPartition);
- String p = mRootPartition.toAbsolutePath().toString();
- String result = runCommand("/system/bin/resize2fs", "-P", p);
- // The return value is the number of 4k block
- try {
- long minSize =
- Long.parseLong(result.lines().toArray(String[]::new)[1].substring(42))
- * 4
- * 1024;
- return roundUp(minSize);
- } catch (NumberFormatException e) {
- Log.e(TAG, "Failed to parse min size, p=" + p + ", result=" + result);
- throw new IOException(e);
- }
- }
-
- public long resize(long desiredSize) throws IOException {
- desiredSize = roundUp(desiredSize);
- final long curSize = getSize();
-
- if (desiredSize == curSize) {
- return desiredSize;
- }
-
- runE2fsck(mRootPartition);
- if (desiredSize > curSize) {
- allocateSpace(mRootPartition, desiredSize);
- }
- resizeFilesystem(mRootPartition, desiredSize);
- return getSize();
- }
-
- private static void allocateSpace(Path path, long sizeInBytes) throws IOException {
- try {
- RandomAccessFile raf = new RandomAccessFile(path.toFile(), "rw");
- FileDescriptor fd = raf.getFD();
- Os.posix_fallocate(fd, 0, sizeInBytes);
- raf.close();
- Log.d(TAG, "Allocated space to: " + sizeInBytes + " bytes");
- } catch (ErrnoException e) {
- Log.e(TAG, "Failed to allocate space", e);
- throw new IOException("Failed to allocate space", e);
- }
- }
-
- private static void runE2fsck(Path path) throws IOException {
- String p = path.toAbsolutePath().toString();
- runCommand("/system/bin/e2fsck", "-y", "-f", p);
- Log.d(TAG, "e2fsck completed: " + path);
- }
-
- private static void resizeFilesystem(Path path, long sizeInBytes) throws IOException {
- long sizeInMB = sizeInBytes / (1024 * 1024);
- if (sizeInMB == 0) {
- Log.e(TAG, "Invalid size: " + sizeInBytes + " bytes");
- throw new IllegalArgumentException("Size cannot be zero MB");
- }
- String sizeArg = sizeInMB + "M";
- String p = path.toAbsolutePath().toString();
- runCommand("/system/bin/resize2fs", p, sizeArg);
- Log.d(TAG, "resize2fs completed: " + path + ", size: " + sizeArg);
- }
-
- private static String runCommand(String... command) throws IOException {
- try {
- Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
- process.waitFor();
- String result = new String(process.getInputStream().readAllBytes());
- if (process.exitValue() != 0) {
- Log.w(TAG, "Process returned with error, command=" + String.join(" ", command)
- + ", exitValue=" + process.exitValue() + ", result=" + result);
- }
- return result;
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new IOException("Command interrupted", e);
- }
- }
-
- private static long roundUp(long bytes) {
- // Round up every diskSizeStep MB
- return (long) Math.ceil(((double) bytes) / RESIZE_STEP_BYTES) * RESIZE_STEP_BYTES;
- }
-}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
new file mode 100644
index 0000000..e52f996
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2024 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.virtualization.terminal
+
+import android.content.Context
+import android.os.FileUtils
+import android.system.ErrnoException
+import android.system.Os
+import android.util.Log
+import com.android.virtualization.terminal.MainActivity.TAG
+import java.io.BufferedReader
+import java.io.FileReader
+import java.io.IOException
+import java.io.RandomAccessFile
+import java.lang.IllegalArgumentException
+import java.lang.NumberFormatException
+import java.lang.RuntimeException
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.StandardCopyOption
+import kotlin.math.ceil
+
+/** Collection of files that consist of a VM image. */
+internal class InstalledImage private constructor(val installDir: Path) {
+ private val rootPartition: Path = installDir.resolve(ROOTFS_FILENAME)
+ val backupFile: Path = installDir.resolve(BACKUP_FILENAME)
+
+ /** The path to the VM config file. */
+ val configPath: Path = installDir.resolve(CONFIG_FILENAME)
+ private val marker: Path = installDir.resolve(MARKER_FILENAME)
+ /** The build ID of the installed image */
+ val buildId: String by lazy { readBuildId() }
+
+ /** Tests if this InstalledImage is actually installed. */
+ fun isInstalled(): Boolean {
+ return Files.exists(marker)
+ }
+
+ /** Fully uninstall this InstalledImage by deleting everything. */
+ @Throws(IOException::class)
+ fun uninstallFully() {
+ FileUtils.deleteContentsAndDir(installDir.toFile())
+ }
+
+ private fun readBuildId(): String {
+ val file = installDir.resolve(BUILD_ID_FILENAME)
+ if (!Files.exists(file)) {
+ return "<no build id>"
+ }
+ try {
+ BufferedReader(FileReader(file.toFile())).use { r ->
+ return r.readLine()
+ }
+ } catch (e: IOException) {
+ throw RuntimeException("Failed to read build ID", e)
+ }
+ }
+
+ @Throws(IOException::class)
+ fun uninstallAndBackup(): Path {
+ Files.delete(marker)
+ Files.move(rootPartition, backupFile, StandardCopyOption.REPLACE_EXISTING)
+ return backupFile
+ }
+
+ fun hasBackup(): Boolean {
+ return Files.exists(backupFile)
+ }
+
+ @Throws(IOException::class)
+ fun deleteBackup() {
+ Files.deleteIfExists(backupFile)
+ }
+
+ @Throws(IOException::class)
+ fun getSize(): Long {
+ return Files.size(rootPartition)
+ }
+
+ @Throws(IOException::class)
+ fun getSmallestSizePossible(): Long {
+ runE2fsck(rootPartition)
+ val p: String = rootPartition.toAbsolutePath().toString()
+ val result = runCommand("/system/bin/resize2fs", "-P", p)
+ // The return value is the number of 4k block
+ return try {
+ roundUp(result.lines().first().substring(42).toLong() * 4 * 1024)
+ } catch (e: NumberFormatException) {
+ Log.e(TAG, "Failed to parse min size, p=$p, result=$result")
+ throw IOException(e)
+ }
+ }
+
+ @Throws(IOException::class)
+ fun resize(desiredSize: Long): Long {
+ val roundedUpDesiredSize = roundUp(desiredSize)
+ val curSize = getSize()
+
+ if (roundedUpDesiredSize == curSize) {
+ return roundedUpDesiredSize
+ }
+
+ runE2fsck(rootPartition)
+ if (roundedUpDesiredSize > curSize) {
+ allocateSpace(rootPartition, roundedUpDesiredSize)
+ }
+ resizeFilesystem(rootPartition, roundedUpDesiredSize)
+ return getSize()
+ }
+
+ companion object {
+ private const val INSTALL_DIRNAME = "linux"
+ private const val ROOTFS_FILENAME = "root_part"
+ private const val BACKUP_FILENAME = "root_part_backup"
+ private const val CONFIG_FILENAME = "vm_config.json"
+ private const val BUILD_ID_FILENAME = "build_id"
+ const val MARKER_FILENAME: String = "completed"
+
+ const val RESIZE_STEP_BYTES: Long = 4 shl 20 // 4 MiB
+
+ /** Returns InstalledImage for a given app context */
+ @JvmStatic
+ fun getDefault(context: Context): InstalledImage {
+ val installDir = context.getFilesDir().toPath().resolve(INSTALL_DIRNAME)
+ return InstalledImage(installDir)
+ }
+
+ @Throws(IOException::class)
+ private fun allocateSpace(path: Path, sizeInBytes: Long) {
+ try {
+ val raf = RandomAccessFile(path.toFile(), "rw")
+ val fd = raf.fd
+ Os.posix_fallocate(fd, 0, sizeInBytes)
+ raf.close()
+ Log.d(TAG, "Allocated space to: $sizeInBytes bytes")
+ } catch (e: ErrnoException) {
+ Log.e(TAG, "Failed to allocate space", e)
+ throw IOException("Failed to allocate space", e)
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun runE2fsck(path: Path) {
+ val p: String = path.toAbsolutePath().toString()
+ runCommand("/system/bin/e2fsck", "-y", "-f", p)
+ Log.d(TAG, "e2fsck completed: $path")
+ }
+
+ @Throws(IOException::class)
+ private fun resizeFilesystem(path: Path, sizeInBytes: Long) {
+ val sizeInMB = sizeInBytes / (1024 * 1024)
+ if (sizeInMB == 0L) {
+ Log.e(TAG, "Invalid size: $sizeInBytes bytes")
+ throw IllegalArgumentException("Size cannot be zero MB")
+ }
+ val sizeArg = sizeInMB.toString() + "M"
+ val p: String = path.toAbsolutePath().toString()
+ runCommand("/system/bin/resize2fs", p, sizeArg)
+ Log.d(TAG, "resize2fs completed: $path, size: $sizeArg")
+ }
+
+ @Throws(IOException::class)
+ private fun runCommand(vararg command: String): String {
+ try {
+ val process = ProcessBuilder(*command).redirectErrorStream(true).start()
+ process.waitFor()
+ val result = String(process.inputStream.readAllBytes())
+ if (process.exitValue() != 0) {
+ Log.w(
+ TAG,
+ "Process returned with error, command=${listOf(*command).joinToString(" ")}," +
+ "exitValue=${process.exitValue()}, result=$result",
+ )
+ }
+ return result
+ } catch (e: InterruptedException) {
+ Thread.currentThread().interrupt()
+ throw IOException("Command interrupted", e)
+ }
+ }
+
+ private fun roundUp(bytes: Long): Long {
+ // Round up every diskSizeStep MB
+ return ceil((bytes.toDouble()) / RESIZE_STEP_BYTES).toLong() * RESIZE_STEP_BYTES
+ }
+ }
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalExceptionHandler.java b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalExceptionHandler.java
deleted file mode 100644
index 4ab2b77..0000000
--- a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalExceptionHandler.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2024 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.virtualization.terminal;
-
-import android.content.Context;
-import android.util.Log;
-
-public class TerminalExceptionHandler implements Thread.UncaughtExceptionHandler {
- private static final String TAG = "TerminalExceptionHandler";
-
- private final Context mContext;
-
- public TerminalExceptionHandler(Context context) {
- mContext = context;
- }
-
- @Override
- public void uncaughtException(Thread thread, Throwable throwable) {
- Exception exception;
- if (throwable instanceof Exception) {
- exception = (Exception) throwable;
- } else {
- exception = new Exception(throwable);
- }
- try {
- ErrorActivity.start(mContext, exception);
- } catch (Exception ex) {
- Log.wtf(TAG, "Failed to launch error activity for an exception", exception);
- }
-
- thread.getDefaultUncaughtExceptionHandler().uncaughtException(thread, throwable);
- }
-}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalExceptionHandler.kt b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalExceptionHandler.kt
new file mode 100644
index 0000000..3a8c444
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalExceptionHandler.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2024 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.virtualization.terminal
+
+import android.content.Context
+import android.util.Log
+import java.lang.Exception
+
+class TerminalExceptionHandler(private val context: Context) : Thread.UncaughtExceptionHandler {
+
+ override fun uncaughtException(thread: Thread, throwable: Throwable) {
+ val exception = (throwable as? Exception) ?: Exception(throwable)
+ try {
+ ErrorActivity.start(context, exception)
+ } catch (_: Exception) {
+ Log.wtf(TAG, "Failed to launch error activity for an exception", exception)
+ }
+ Thread.getDefaultUncaughtExceptionHandler()?.uncaughtException(thread, throwable)
+ }
+
+ companion object {
+ private const val TAG = "TerminalExceptionHandler"
+ }
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalThreadFactory.java b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalThreadFactory.java
deleted file mode 100644
index 5ee535d..0000000
--- a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalThreadFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2024 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.virtualization.terminal;
-
-import android.content.Context;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-
-public class TerminalThreadFactory implements ThreadFactory {
- private final Context mContext;
-
- public TerminalThreadFactory(Context context) {
- mContext = context;
- }
-
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = Executors.defaultThreadFactory().newThread(r);
- thread.setUncaughtExceptionHandler(new TerminalExceptionHandler(mContext));
- return thread;
- }
-}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/TerminalThreadFactory.kt b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalThreadFactory.kt
new file mode 100644
index 0000000..f8e909d
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/TerminalThreadFactory.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 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.virtualization.terminal
+
+import android.content.Context
+import java.util.concurrent.Executors
+import java.util.concurrent.ThreadFactory
+
+class TerminalThreadFactory(private val context: Context) : ThreadFactory {
+ override fun newThread(r: Runnable): Thread {
+ return Executors.defaultThreadFactory().newThread(r).also {
+ it.uncaughtExceptionHandler = TerminalExceptionHandler(context)
+ }
+ }
+}
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 3db6a40..613f7d2 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -57,18 +57,16 @@
;;
esac
if [[ "${*:$OPTIND:1}" ]]; then
- built_image="${*:$OPTIND:1}"
+ output="${*:$OPTIND:1}"
fi
}
prepare_build_id() {
- local filename=build_id
if [ -z "${KOKORO_BUILD_NUMBER}" ]; then
- echo eng-$(hostname)-$(date --utc) > ${filename}
+ echo eng-$(hostname)-$(date --utc)
else
- echo ${KOKORO_BUILD_NUMBER} > ${filename}
+ echo ${KOKORO_BUILD_NUMBER}
fi
- echo ${filename}
}
install_prerequisites() {
@@ -302,17 +300,22 @@
}
run_fai() {
- local out="${built_image}"
+ local out="${raw_disk_image}"
make -C "${debian_cloud_image}" "image_bookworm_nocloud_${debian_arch}"
mv "${debian_cloud_image}/image_bookworm_nocloud_${debian_arch}.raw" "${out}"
}
-extract_partitions() {
+generate_output_package() {
+ fdisk -l "${raw_disk_image}"
root_partition_num=1
bios_partition_num=14
efi_partition_num=15
- loop=$(losetup -f --show --partscan $built_image)
+ pushd ${workdir} > /dev/null
+
+ echo ${build_id} > build_id
+
+ loop=$(losetup -f --show --partscan $raw_disk_image)
dd if="${loop}p$root_partition_num" of=root_part
if [[ "$arch" == "x86_64" ]]; then
dd if="${loop}p$bios_partition_num" of=bios_part
@@ -320,11 +323,38 @@
dd if="${loop}p$efi_partition_num" of=efi_part
losetup -d "${loop}"
- sed -i "s/{root_part_guid}/$(sfdisk --part-uuid $built_image $root_partition_num)/g" vm_config.json
+ cp ${vm_config} vm_config.json
+ sed -i "s/{root_part_guid}/$(sfdisk --part-uuid $raw_disk_image $root_partition_num)/g" vm_config.json
if [[ "$arch" == "x86_64" ]]; then
- sed -i "s/{bios_part_guid}/$(sfdisk --part-uuid $built_image $bios_partition_num)/g" vm_config.json
+ sed -i "s/{bios_part_guid}/$(sfdisk --part-uuid $raw_disk_image $bios_partition_num)/g" vm_config.json
fi
- sed -i "s/{efi_part_guid}/$(sfdisk --part-uuid $built_image $efi_partition_num)/g" vm_config.json
+ sed -i "s/{efi_part_guid}/$(sfdisk --part-uuid $raw_disk_image $efi_partition_num)/g" vm_config.json
+
+ images=()
+ if [[ "$arch" == "aarch64" ]]; then
+ images+=(
+ root_part
+ efi_part
+ )
+ # TODO(b/365955006): remove these lines when uboot supports x86_64 EFI application
+ elif [[ "$arch" == "x86_64" ]]; then
+ rm -f vmlinuz initrd.img
+ virt-get-kernel -a "${raw_disk_image}"
+ mv vmlinuz* vmlinuz
+ mv initrd.img* initrd.img
+ images+=(
+ bios_part
+ root_part
+ efi_part
+ vmlinuz
+ initrd.img
+ )
+ fi
+
+ popd > /dev/null
+
+ # --sparse option isn't supported in apache-commons-compress
+ tar czv -f ${output} -C ${workdir} build_id "${images[@]}" vm_config.json
}
clean_up() {
@@ -334,14 +364,16 @@
set -e
trap clean_up EXIT
-built_image=image.raw
+output=images.tar.gz
workdir=$(mktemp -d)
+raw_disk_image=${workdir}/image.raw
build_id=$(prepare_build_id)
debian_cloud_image=${workdir}/debian_cloud_image
debian_version=bookworm
config_space=${debian_cloud_image}/config_space/${debian_version}
resources_dir=${debian_cloud_image}/src/debian_cloud_images/resources
arch="$(uname -m)"
+vm_config="$(realpath $(dirname "$0"))/vm_config.json.${arch}"
mode=debug
save_workdir=0
use_custom_kernel=0
@@ -353,32 +385,4 @@
copy_android_config
package_custom_kernel
run_fai
-fdisk -l "${built_image}"
-images=()
-
-cp "$(dirname "$0")/vm_config.json.${arch}" vm_config.json
-
-extract_partitions
-
-if [[ "$arch" == "aarch64" ]]; then
- images+=(
- root_part
- efi_part
- )
-# TODO(b/365955006): remove these lines when uboot supports x86_64 EFI application
-elif [[ "$arch" == "x86_64" ]]; then
- rm -f vmlinuz initrd.img
- virt-get-kernel -a "${built_image}"
- mv vmlinuz* vmlinuz
- mv initrd.img* initrd.img
- images+=(
- bios_part
- root_part
- efi_part
- vmlinuz
- initrd.img
- )
-fi
-
-# --sparse option isn't supported in apache-commons-compress
-tar czv -f images.tar.gz ${build_id} "${images[@]}" vm_config.json
+generate_output_package
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
index 6b54bf7..ef57325 100644
--- a/libs/libavf/include/android/virtualization.h
+++ b/libs/libavf/include/android/virtualization.h
@@ -70,7 +70,13 @@
const char* _Nonnull name) __INTRODUCED_IN(36);
/**
- * Set an instance ID of a virtual machine.
+ * Set an instance ID of a virtual machine. Every virtual machine is identified by a unique
+ * `instanceId` which the virtual machine uses as its persistent identity while performing stateful
+ * operations that are expected to outlast single boot of the VM. For example, some virtual machines
+ * use it as a `Id` for storing secrets in Secretkeeper, which are retrieved on next boot of th VM.
+ *
+ * The `instanceId` is expected to be re-used for the VM instance with an associated state (secret,
+ * encrypted storage) - i.e., rebooting the VM must not change the instanceId.
*
* \param config a virtual machine config object.
* \param instanceId a pointer to a 64-byte buffer for the instance ID.