Merge "Run a non-protected VM at DebugLevel::APP_ONLY"
diff --git a/compos/common/timeouts.rs b/compos/common/timeouts.rs
index b3ec1e5..bdabb1e 100644
--- a/compos/common/timeouts.rs
+++ b/compos/common/timeouts.rs
@@ -26,8 +26,6 @@
 pub struct Timeouts {
     /// Total time that odrefresh may take to perform compilation
     pub odrefresh_max_execution_time: Duration,
-    /// Time allowed for a single compilation step run by odrefresh
-    pub odrefresh_max_child_process_time: Duration,
     /// Time allowed for the CompOS VM to start up and become ready.
     pub vm_max_time_to_ready: Duration,
 }
@@ -55,13 +53,11 @@
 pub const NORMAL_TIMEOUTS: Timeouts = Timeouts {
     // Note: the source of truth for these odrefresh timeouts is art/odrefresh/odr_config.h.
     odrefresh_max_execution_time: Duration::from_secs(300),
-    odrefresh_max_child_process_time: Duration::from_secs(90),
-    vm_max_time_to_ready: Duration::from_secs(20),
+    vm_max_time_to_ready: Duration::from_secs(15),
 };
 
 /// The timeouts that we use when need_extra_time() returns true.
 pub const EXTENDED_TIMEOUTS: Timeouts = Timeouts {
     odrefresh_max_execution_time: Duration::from_secs(480),
-    odrefresh_max_child_process_time: Duration::from_secs(150),
     vm_max_time_to_ready: Duration::from_secs(120),
 };
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index f598034..be97ad5 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -83,7 +83,7 @@
   /data/local/tmp/virt/instance.img assets/vm_config.json
 ```
 
-## Building and updating CrosVM and VirtualizationService
+## Building and updating CrosVM and VirtualizationService {#building-and-updating}
 
 You can update CrosVM and the VirtualizationService by updating the `com.android.virt` APEX instead
 of rebuilding the entire image.
@@ -94,3 +94,28 @@
 adb install out/dist/com.android.virt.apex
 adb reboot
 ```
+
+## Building and updating GKI inside Microdroid
+
+Checkout the Android common kernel and build it following the [official
+guideline](https://source.android.com/setup/build/building-kernels).
+
+```shell
+mkdir android-kernel && cd android-kernel
+repo init -u https://android.googlesource.com/kernel/manifest -b common-android12-5.10
+repo sync
+FAST_BUILD=1 DIST_DIR=out/dist BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh -j80
+```
+
+Replace `build.config.gki.aarch64` with `build.config.gki.x86_64` if building
+for x86.
+
+Then copy the built kernel to the Android source tree.
+
+```
+cp out/dist/Image <android_root>/kernel/prebuilts/5.10/arm64/kernel-5.10
+```
+
+Finally rebuild the `com.android.virt` APEX and install it by following the
+steps shown in [Building and updating Crosvm and
+Virtualization](#building-and-updating).
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index 65ce7ea..ed2c2a1 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -36,6 +36,8 @@
 import android.system.virtualizationservice.VirtualMachineState;
 import android.util.JsonReader;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -52,6 +54,8 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
 import java.util.zip.ZipFile;
 
 /**
@@ -92,6 +96,9 @@
         DELETED,
     }
 
+    /** Lock for internal synchronization. */
+    private final Object mLock = new Object();
+
     /** The package which owns this VM. */
     private final @NonNull String mPackageName;
 
@@ -135,9 +142,11 @@
     private @Nullable IVirtualMachine mVirtualMachine;
 
     /** The registered callback */
+    @GuardedBy("mLock")
     private @Nullable VirtualMachineCallback mCallback;
 
     /** The executor on which the callback will be executed */
+    @GuardedBy("mLock")
     private @Nullable Executor mCallbackExecutor;
 
     private @Nullable ParcelFileDescriptor mConsoleReader;
@@ -299,20 +308,37 @@
     public void setCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull VirtualMachineCallback callback) {
-        mCallbackExecutor = executor;
-        mCallback = callback;
+        synchronized (mLock) {
+            mCallback = callback;
+            mCallbackExecutor = executor;
+        }
     }
 
     /** Clears the currently registered callback. */
     public void clearCallback() {
-        // TODO(b/220730550): synchronize with the callers of the callback
-        mCallback = null;
-        mCallbackExecutor = null;
+        synchronized (mLock) {
+            mCallback = null;
+            mCallbackExecutor = null;
+        }
     }
 
-    /** Returns the currently registered callback. */
-    public @Nullable VirtualMachineCallback getCallback() {
-        return mCallback;
+    /** Executes a callback on the callback executor. */
+    private void executeCallback(Consumer<VirtualMachineCallback> fn) {
+        final VirtualMachineCallback callback;
+        final Executor executor;
+        synchronized (mLock) {
+            callback = mCallback;
+            executor = mCallbackExecutor;
+        }
+        if (callback == null || executor == null) {
+            return;
+        }
+        final long restoreToken = Binder.clearCallingIdentity();
+        try {
+            executor.execute(() -> fn.accept(callback));
+        } finally {
+            Binder.restoreCallingIdentity(restoreToken);
+        }
     }
 
     /**
@@ -376,14 +402,15 @@
             android.system.virtualizationservice.VirtualMachineConfig vmConfigParcel =
                     android.system.virtualizationservice.VirtualMachineConfig.appConfig(appConfig);
 
+            // The VM should only be observed to die once
+            AtomicBoolean onDiedCalled = new AtomicBoolean(false);
+
             IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
                 @Override
                 public void binderDied() {
-                    final VirtualMachineCallback cb = mCallback;
-                    if (cb != null) {
-                        // TODO(b/220730550): don't call if the VM already died
-                        cb.onDied(VirtualMachine.this, VirtualMachineCallback
-                                .DEATH_REASON_VIRTUALIZATIONSERVICE_DIED);
+                    if (onDiedCalled.compareAndSet(false, true)) {
+                        executeCallback((cb) -> cb.onDied(VirtualMachine.this,
+                                VirtualMachineCallback.DEATH_REASON_VIRTUALIZATIONSERVICE_DIED));
                     }
                 }
             };
@@ -393,80 +420,32 @@
                     new IVirtualMachineCallback.Stub() {
                         @Override
                         public void onPayloadStarted(int cid, ParcelFileDescriptor stream) {
-                            final VirtualMachineCallback cb = mCallback;
-                            if (cb == null) {
-                                return;
-                            }
-                            final long restoreToken = Binder.clearCallingIdentity();
-                            try {
-                                mCallbackExecutor.execute(
-                                        () -> cb.onPayloadStarted(VirtualMachine.this, stream));
-                            } finally {
-                                Binder.restoreCallingIdentity(restoreToken);
-                            }
+                            executeCallback(
+                                    (cb) -> cb.onPayloadStarted(VirtualMachine.this, stream));
                         }
-
                         @Override
                         public void onPayloadReady(int cid) {
-                            final VirtualMachineCallback cb = mCallback;
-                            if (cb == null) {
-                                return;
-                            }
-                            final long restoreToken = Binder.clearCallingIdentity();
-                            try {
-                                mCallbackExecutor.execute(
-                                        () -> cb.onPayloadReady(VirtualMachine.this));
-                            } finally {
-                                Binder.restoreCallingIdentity(restoreToken);
-                            }
+                            executeCallback((cb) -> cb.onPayloadReady(VirtualMachine.this));
                         }
-
                         @Override
                         public void onPayloadFinished(int cid, int exitCode) {
-                            final VirtualMachineCallback cb = mCallback;
-                            if (cb == null) {
-                                return;
-                            }
-                            final long restoreToken = Binder.clearCallingIdentity();
-                            try {
-                                mCallbackExecutor.execute(
-                                        () -> cb.onPayloadFinished(VirtualMachine.this, exitCode));
-                            } finally {
-                                Binder.restoreCallingIdentity(restoreToken);
-                            }
+                            executeCallback(
+                                    (cb) -> cb.onPayloadFinished(VirtualMachine.this, exitCode));
                         }
-
                         @Override
                         public void onError(int cid, int errorCode, String message) {
-                            final VirtualMachineCallback cb = mCallback;
-                            if (cb == null) {
-                                return;
-                            }
-                            final long restoreToken = Binder.clearCallingIdentity();
-                            try {
-                                mCallbackExecutor.execute(
-                                        () -> cb.onError(VirtualMachine.this, errorCode, message));
-                            } finally {
-                                Binder.restoreCallingIdentity(restoreToken);
-                            }
+                            executeCallback(
+                                    (cb) -> cb.onError(VirtualMachine.this, errorCode, message));
                         }
-
                         @Override
                         public void onDied(int cid, int reason) {
                             service.asBinder().unlinkToDeath(deathRecipient, 0);
-                            final VirtualMachineCallback cb = mCallback;
-                            if (cb == null) {
-                                return;
-                            }
-                            final long restoreToken = Binder.clearCallingIdentity();
-                            try {
-                                mCallbackExecutor.execute(
-                                        () -> cb.onDied(VirtualMachine.this, reason));
-                            } finally {
-                                Binder.restoreCallingIdentity(restoreToken);
+                            if (onDiedCalled.compareAndSet(false, true)) {
+                                executeCallback((cb) -> cb.onDied(VirtualMachine.this, reason));
                             }
                         }
-                    });
+                    }
+            );
             service.asBinder().linkToDeath(deathRecipient, 0);
             mVirtualMachine.start();
         } catch (IOException e) {
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 0ca7036..4631cf8 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -70,6 +70,7 @@
         "libartpalette-system",
 
         "apexd",
+        "atrace",
         "debuggerd",
         "diced.microdroid",
         "linker",
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 2ccdc3b..f6d5092 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -24,10 +24,8 @@
     chmod 0755 /dev/binderfs
 
     symlink /dev/binderfs/binder /dev/binder
-    symlink /dev/binderfs/hwbinder /dev/hwbinder
     symlink /dev/binderfs/vndbinder /dev/vndbinder
 
-    chmod 0666 /dev/binderfs/hwbinder
     chmod 0666 /dev/binderfs/binder
     chmod 0666 /dev/binderfs/vndbinder