Merge "Use chromiumos_test_image.tar.xz instead of image.zip" into main
diff --git a/docs/custom_vm.md b/docs/custom_vm.md
index 077d388..945798f 100644
--- a/docs/custom_vm.md
+++ b/docs/custom_vm.md
@@ -65,25 +65,20 @@
 
 #### Download from build server
 
-  - Step 1) Go to the link https://ci.chromium.org/ui/p/chromeos/builders/chromiumos/ferrochrome-public-main/
-    - Note: I 'searched' the ferrochrome target with builder search.
-  - Step 2) Click a build number
-  - Step 3) Expand steps and find `48. upload artifacts`.
-  - Step 4) Click `gs upload dir`. You'll see Cloud storage with comprehensive artifacts (e.g. [Here](https://pantheon.corp.google.com/storage/browser/chromiumos-image-archive/ferrochrome-public/R126-15883.0.0) is the initial build of ferrochrome)
-  - Step 5) Download `image.zip`, which contains working vmlinuz.
-    - Note: DO NOT DOWNLOAD `vmlinuz.tar.xz` from the CI.
-  - Step 6) Uncompress `image.zip`, and boot with `chromiumos_test_image.bin` and `boot_images/vmlinuz`.
-    - Note: DO NOT USE `vmlinuz.bin`.
+Here's the link the comprehensive artifacts
+https://pantheon.corp.google.com/storage/browser/chromiumos-image-archive/ferrochrome-public
 
-IMPORTANT: DO NOT USE `vmlinuz.bin` for passing to crosvm. It doesn't pick-up the correct `init` process (picks `/init` instead of `/sbin/init`, and `cfg80211` keeps crashing (i.e. no network)
+Pick a build, download, and untar `chromiumos_test_image.tar.xz`. We'll boot with `chromiumos_test_image.bin` in it.
 
+To find the latest green build, check following:
+https://pantheon.corp.google.com/storage/browser/_details/chromiumos-image-archive/ferrochrome-public/LATEST-main
 
 #### Build ChromiumOS for VM
 
 First, check out source code from the ChromiumOS and Chromium projects.
 
+* Checking out Chromium: https://www.chromium.org/developers/how-tos/get-the-code/
 * Checking out ChromiumOS: https://www.chromium.org/chromium-os/developer-library/guides/development/developer-guide/
-* Checking out Chromium: https://g3doc.corp.google.com/chrome/chromeos/system_services_team/dev_instructions/g3doc/setup_checkout.md?cl=head
 
 Important: When you are at the step “Set up gclient args” in the Chromium checkout instruction, configure .gclient as follows.
 
@@ -95,9 +90,7 @@
     "url": "https://chromium.googlesource.com/chromium/src.git",
     "managed": False,
     "custom_deps": {},
-    "custom_vars": {
-      "checkout_src_internal": True,
-    },
+    "custom_vars": {},
   },
 ]
 target_os = ['chromeos']
@@ -162,10 +155,7 @@
 
 Don’t forget to call `build-image` afterwards.
 
-You need two outputs:
-
-* ChromiumOS disk image: ~/chromiumos/src/build/images/ferrochrome/latest/chromiumos_test_image.bin
-* The kernel: ~/chromiumos/src/build/images/ferrochrome/latest/boot_images/vmlinuz
+You need ChromiumOS disk image: ~/chromiumos/src/build/images/ferrochrome/latest/chromiumos_test_image.bin
 
 ### Create a guest VM configuration
 
@@ -173,7 +163,6 @@
 
 ```
 $ adb push  ~/chromiumos/src/build/images/ferrochrome/latest/chromiumos_test_image.bin /data/local/tmp/
-$ adb push ~/chromiumos/out/build/ferrochrome/boot/vmlinuz /data/local/tmp/kernel
 ```
 
 Create a VM config file as below.
@@ -182,7 +171,6 @@
 $ cat > vm_config.json; adb push vm_config.json /data/local/tmp
 {
     "name": "cros",
-    "kernel": "/data/local/tmp/kernel",
     "disks": [
         {
             "image": "/data/local/tmp/chromiumos_test_image.bin",
diff --git a/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java b/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
index 769e74f..8ca39eb 100644
--- a/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
+++ b/ferrochrome_app/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
@@ -45,7 +45,7 @@
 public class FerrochromeActivity extends Activity {
     ExecutorService executorService = Executors.newSingleThreadExecutor();
     private static final String TAG = "FerrochromeActivity";
-    private static final String FERROCHROME_VERSION = "R127-15916.0.0";
+    private static final String FERROCHROME_VERSION = "R128-15926.0.0";
     private static final String EXTERNAL_STORAGE_DIR =
             Environment.getExternalStorageDirectory().getPath() + File.separator;
     private static final Path IMAGE_PATH =
@@ -68,20 +68,23 @@
                 () -> {
                     if (Files.notExists(IMAGE_PATH)
                             || !FERROCHROME_VERSION.equals(getVersionInfo())) {
-                        updateStatus("image doesn't exist");
-                        updateStatus("download image");
+                        updateStatus("Starting first-time setup.");
+                        updateStatus(
+                                "Downloading Ferrochrome image. This can take about 5 to 10"
+                                        + " minutes, depending on your network speed.");
                         if (download(FERROCHROME_VERSION)) {
-                            updateStatus("download done");
+                            updateStatus("Done.");
                         } else {
-                            updateStatus("download failed, check internet connection and retry");
+                            updateStatus(
+                                    "Download failed. Check the internet connection and retry.");
                             return;
                         }
                     } else {
-                        updateStatus("there are already downloaded images");
+                        updateStatus("Ferrochrome is already downloaded.");
                     }
-                    updateStatus("write down vm config");
+                    updateStatus("Updating VM config.");
                     copyVmConfigJson();
-                    updateStatus("custom_vm_setup: copy files to /data/local/tmp");
+                    updateStatus("Updating VM images. This may take a few minutes.");
                     SystemProperties.set("debug.custom_vm_setup.start", "true");
                     while (!SystemProperties.getBoolean("debug.custom_vm_setup.done", false)) {
                         // Wait for custom_vm_setup
@@ -90,10 +93,9 @@
                         } catch (Exception e) {
                             Log.d(TAG, e.toString());
                         }
-                        updateStatus("wait for custom_vm_setup");
                     }
-                    updateStatus("enable vmlauncher");
-                    updateStatus("ready for ferrochrome");
+                    updateStatus("Done.");
+                    updateStatus("Starting Ferrochrome...");
                     runOnUiThread(
                             () ->
                                     startActivity(
@@ -131,9 +133,6 @@
     }
 
     private void copyVmConfigJson() {
-        if (Files.exists(VM_CONFIG_PATH)) {
-            return;
-        }
         try (InputStream is = getResources().openRawResource(R.raw.vm_config)) {
             Files.copy(is, VM_CONFIG_PATH, StandardCopyOption.REPLACE_EXISTING);
         } catch (IOException e) {
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachine.java b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
index 195c538..bca36a4 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachine.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
@@ -1137,7 +1137,9 @@
     /**
      * Runs this virtual machine. The returning of this method however doesn't mean that the VM has
      * actually started running or the OS has booted there. Such events can be notified by
-     * registering a callback using {@link #setCallback} before calling {@code run()}.
+     * registering a callback using {@link #setCallback} before calling {@code run()}. There is no
+     * limit other than available memory that limits the number of virtual machines that can run at
+     * the same time.
      *
      * <p>NOTE: This method may block and should not be called on the main thread.
      *
diff --git a/tests/ferrochrome/ferrochrome.sh b/tests/ferrochrome/ferrochrome.sh
index 5638b34..92702ee 100755
--- a/tests/ferrochrome/ferrochrome.sh
+++ b/tests/ferrochrome/ferrochrome.sh
@@ -100,13 +100,9 @@
     # DISCLAIMER: Image is too large (1.5G+ for compressed, 6.5G+ for uncompressed), so can't submit.
     fecr_dir=$(mktemp -d)
 
-    echo "Downloading ferrochrome image to ${fecr_dir}"
+    echo "Downloading & extracting ferrochrome image to ${fecr_dir}"
     fecr_version=${fecr_version:-${FECR_DEFAULT_VERSION}}
-    curl --output-dir ${fecr_dir} -O ${FECR_GS_URL}/${fecr_version}/chromiumos_test_image.tar.xz
-  fi
-  if [[ ! -f "${fecr_dir}/chromiumos_test_image.bin" ]]; then
-    echo "Extrating ferrochrome image"
-    tar xvf ${fecr_dir}/chromiumos_test_image.tar.xz -C ${fecr_dir} > /dev/null
+    curl ${FECR_GS_URL}/${fecr_version}/chromiumos_test_image.tar.xz | tar xfJ - -C ${fecr_dir}
   fi
 
   echo "Pushing ferrochrome image to ${FECR_DEVICE_DIR}"
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 55badcc..5b67083 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -37,6 +37,7 @@
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static java.util.stream.Collectors.toList;
 
+import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.ComponentName;
@@ -2512,6 +2513,41 @@
         }
     }
 
+    @Test
+    public void concurrentVms() throws Exception {
+        final long vmSize = minMemoryRequired();
+        final int numVMs = 8;
+        final long availableMem = getAvailableMemory();
+
+        // Let's not use more than half of the available memory
+        assume().withMessage("Available memory (" + availableMem + " bytes) too small")
+                .that((numVMs * vmSize) <= (availableMem / 2))
+                .isTrue();
+
+        VirtualMachine[] vms = new VirtualMachine[numVMs];
+        for (int i = 0; i < numVMs; i++) {
+            VirtualMachineConfig config =
+                    newVmConfigBuilderWithPayloadBinary("MicrodroidIdleNativeLib.so")
+                            .setDebugLevel(DEBUG_LEVEL_NONE)
+                            .setMemoryBytes(vmSize)
+                            .build();
+
+            vms[i] = forceCreateNewVirtualMachine("test_concurrent_vms_" + i, config);
+            vms[i].run();
+        }
+
+        for (VirtualMachine vm : vms) {
+            assertThat(vm.getStatus()).isEqualTo(VirtualMachine.STATUS_RUNNING);
+        }
+    }
+
+    private long getAvailableMemory() {
+        ActivityManager am = getContext().getSystemService(ActivityManager.class);
+        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+        am.getMemoryInfo(memoryInfo);
+        return memoryInfo.availMem;
+    }
+
     private VirtualMachineDescriptor toParcelFromParcel(VirtualMachineDescriptor descriptor) {
         Parcel parcel = Parcel.obtain();
         descriptor.writeToParcel(parcel, 0);
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index af80998..acdb53a 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -34,7 +34,7 @@
     LazyServiceGuard, ParcelFileDescriptor, Status, Strong,
 };
 use lazy_static::lazy_static;
-use libc::VMADDR_CID_HOST;
+use libc::{VMADDR_CID_HOST, VMADDR_CID_HYPERVISOR, VMADDR_CID_LOCAL};
 use log::{error, info, warn};
 use nix::unistd::{chown, Uid};
 use openssl::x509::X509;
@@ -878,11 +878,21 @@
     for incoming_stream in listener.incoming() {
         let mut incoming_stream = match incoming_stream {
             Err(e) => {
-                warn!("invalid incoming connection: {:?}", e);
+                warn!("invalid incoming connection: {e:?}");
                 continue;
             }
             Ok(s) => s,
         };
+        if let Ok(addr) = incoming_stream.peer_addr() {
+            let cid = addr.cid();
+            match cid {
+                VMADDR_CID_LOCAL | VMADDR_CID_HOST | VMADDR_CID_HYPERVISOR => {
+                    warn!("Rejecting non-guest tombstone vsock connection from cid={cid}");
+                    continue;
+                }
+                _ => info!("Vsock Stream connected to cid={cid} for tombstones"),
+            }
+        }
         std::thread::spawn(move || {
             if let Err(e) = handle_tombstone(&mut incoming_stream) {
                 error!("Failed to write tombstone- {:?}", e);
@@ -893,9 +903,6 @@
 }
 
 fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
-    if let Ok(addr) = stream.peer_addr() {
-        info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
-    }
     let tb_connection =
         TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
             .context("Failed to connect to tombstoned")?;
diff --git a/vmlauncher_app/AndroidManifest.xml b/vmlauncher_app/AndroidManifest.xml
index ecfef86..4f95086 100644
--- a/vmlauncher_app/AndroidManifest.xml
+++ b/vmlauncher_app/AndroidManifest.xml
@@ -15,7 +15,7 @@
                   android:exported="true">
         </activity>
         <activity-alias android:name=".MainActivityAlias"
-                android:targetActivity=".MainActivity"
+                android:targetActivity="com.android.virtualization.vmlauncher.MainActivity"
                 android:exported="true"
                 android:enabled="false">
             <intent-filter>