Merge "Rename to PRODUCT_FSVERITY_GENERATE_METADATA"
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index df95323..6863b0c 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -317,22 +317,6 @@
         RunCommand(args, cmd)
 
 
-def SignSuperImg(args, key, super_img, work_dir):
-    # unpack super.img
-    UnpackSuperImg(args, super_img, work_dir)
-
-    system_a_img = os.path.join(work_dir, 'system_a.img')
-    vendor_a_img = os.path.join(work_dir, 'vendor_a.img')
-
-    # re-sign each partition
-    system_a_f = Async(AddHashTreeFooter, args, key, system_a_img)
-    vendor_a_f = Async(AddHashTreeFooter, args, key, vendor_a_img)
-
-    # 3. re-pack super.img
-    partitions = {"system_a": system_a_img, "vendor_a": vendor_a_img}
-    Async(MakeSuperImage, args, partitions, super_img, wait=[system_a_f, vendor_a_f])
-
-
 def ReplaceBootloaderPubkey(args, key, bootloader, bootloader_pubkey):
     if os.path.basename(bootloader) in args.key_overrides:
         key = args.key_overrides[os.path.basename(bootloader)]
@@ -399,13 +383,20 @@
     init_boot_img_f = Async(AddHashFooter, args, key, files['init_boot.img'])
 
     # re-sign super.img
-    super_img_f = Async(SignSuperImg, args, key, files['super.img'], unpack_dir.name)
+    # 1. unpack super.img
+    # 2. resign system and vendor
+    # 3. repack super.img out of resigned system and vendor
+    UnpackSuperImg(args, files['super.img'], unpack_dir.name)
+    system_a_f = Async(AddHashTreeFooter, args, key, system_a_img)
+    vendor_a_f = Async(AddHashTreeFooter, args, key, vendor_a_img)
+    partitions = {"system_a": system_a_img, "vendor_a": vendor_a_img}
+    Async(MakeSuperImage, args, partitions, files['super.img'], wait=[system_a_f, vendor_a_f])
 
     # re-generate vbmeta from re-signed {boot, vendor_boot, init_boot, system_a, vendor_a}.img
     Async(MakeVbmetaImage, args, key, files['vbmeta.img'],
           images=[files['boot.img'], files['vendor_boot.img'],
                   files['init_boot.img'], system_a_img, vendor_a_img],
-          wait=[boot_img_f, vendor_boot_img_f, init_boot_img_f, super_img_f])
+          wait=[boot_img_f, vendor_boot_img_f, init_boot_img_f, system_a_f, vendor_a_f])
 
     # Re-sign bootconfigs and the uboot_env with the same key
     bootconfig_sign_key = key
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 7788702..523da35 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -16,6 +16,7 @@
         "libauthfs_fsverity_metadata",
         "libbinder_rs",
         "libcfg_if",
+        "libclap",
         "libfsverity_digests_proto_rust",
         "libfuse_rust",
         "liblibc",
@@ -24,7 +25,6 @@
         "libopenssl",
         "libprotobuf",
         "librpcbinder_rs",
-        "libstructopt",
         "libthiserror",
     ],
     prefer_rlib: true,
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index c09ed71..9ff0ae3 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -33,13 +33,13 @@
 //! the state is not persistent, thus only new file/directory are supported.
 
 use anyhow::{anyhow, bail, Result};
+use clap::Parser;
 use log::error;
 use protobuf::Message;
 use std::convert::TryInto;
 use std::fs::File;
 use std::num::NonZeroU8;
 use std::path::{Path, PathBuf};
-use structopt::StructOpt;
 
 mod common;
 mod file;
@@ -53,22 +53,21 @@
 use fsverity_digests_proto::fsverity_digests::FSVerityDigests;
 use fusefs::{AuthFs, AuthFsEntry, LazyVerifiedReadonlyFile};
 
-#[derive(StructOpt)]
+#[derive(Parser)]
 struct Args {
     /// Mount point of AuthFS.
-    #[structopt(parse(from_os_str))]
     mount_point: PathBuf,
 
     /// CID of the VM where the service runs.
-    #[structopt(long)]
+    #[clap(long)]
     cid: u32,
 
     /// Extra options to FUSE
-    #[structopt(short = "o")]
+    #[clap(short = 'o')]
     extra_options: Option<String>,
 
     /// Number of threads to serve FUSE requests.
-    #[structopt(short = "j")]
+    #[clap(short = 'j')]
     thread_number: Option<NonZeroU8>,
 
     /// A read-only remote file with integrity check. Can be multiple.
@@ -76,21 +75,21 @@
     /// For example, `--remote-ro-file 5:sha256-1234abcd` tells the filesystem to associate the
     /// file $MOUNTPOINT/5 with a remote FD 5, and has a fs-verity digest with sha256 of the hex
     /// value 1234abcd.
-    #[structopt(long, parse(try_from_str = parse_remote_ro_file_option))]
+    #[clap(long, value_parser = parse_remote_ro_file_option)]
     remote_ro_file: Vec<OptionRemoteRoFile>,
 
     /// A read-only remote file without integrity check. Can be multiple.
     ///
     /// For example, `--remote-ro-file-unverified 5` tells the filesystem to associate the file
     /// $MOUNTPOINT/5 with a remote FD 5.
-    #[structopt(long)]
+    #[clap(long)]
     remote_ro_file_unverified: Vec<i32>,
 
     /// A new read-writable remote file with integrity check. Can be multiple.
     ///
     /// For example, `--remote-new-rw-file 5` tells the filesystem to associate the file
     /// $MOUNTPOINT/5 with a remote FD 5.
-    #[structopt(long)]
+    #[clap(long)]
     remote_new_rw_file: Vec<i32>,
 
     /// A read-only directory that represents a remote directory. The directory view is constructed
@@ -107,7 +106,7 @@
     /// include a file like /5/system/framework/framework.jar. "prefix/" tells the filesystem to
     /// strip the path (e.g. "system/") from the mount point to match the expected location of the
     /// remote FD (e.g. a directory FD of "/system" in the remote).
-    #[structopt(long, parse(try_from_str = parse_remote_new_ro_dir_option))]
+    #[clap(long, value_parser = parse_remote_new_ro_dir_option)]
     remote_ro_dir: Vec<OptionRemoteRoDir>,
 
     /// A new directory that is assumed empty in the backing filesystem. New files created in this
@@ -116,14 +115,15 @@
     ///
     /// For example, `--remote-new-rw-dir 5` tells the filesystem to associate $MOUNTPOINT/5
     /// with a remote dir FD 5.
-    #[structopt(long)]
+    #[clap(long)]
     remote_new_rw_dir: Vec<i32>,
 
     /// Enable debugging features.
-    #[structopt(long)]
+    #[clap(long)]
     debug: bool,
 }
 
+#[derive(Clone)]
 struct OptionRemoteRoFile {
     /// ID to refer to the remote file.
     remote_fd: i32,
@@ -132,6 +132,7 @@
     digest: String,
 }
 
+#[derive(Clone)]
 struct OptionRemoteRoDir {
     /// ID to refer to the remote dir.
     remote_dir_fd: i32,
@@ -305,7 +306,7 @@
 }
 
 fn try_main() -> Result<()> {
-    let args = Args::from_args_safe()?;
+    let args = Args::parse();
 
     let log_level = if args.debug { log::Level::Debug } else { log::Level::Info };
     android_logger::init_once(
diff --git a/compos/Android.bp b/compos/Android.bp
index 6aa9d3d..ea7c4d6 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -13,7 +13,6 @@
         "libandroid_logger",
         "libanyhow",
         "libbinder_rs",
-        "libclap",
         "libcompos_common",
         "liblibc",
         "liblog_rust",
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index d74a6f9..5ea5c06 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -45,9 +45,6 @@
     pub debug_mode: bool,
     /// Number of vCPUs to have in the VM. If None, defaults to 1.
     pub cpus: Option<NonZeroU32>,
-    /// Comma separated list of host CPUs where vCPUs are assigned to. If None, any host CPU can be
-    /// used to run any vCPU.
-    pub cpu_set: Option<String>,
     /// List of task profiles to apply to the VM
     pub task_profiles: Vec<String>,
     /// If present, overrides the path to the VM config JSON file
@@ -112,7 +109,6 @@
             protectedVm: protected_vm,
             memoryMib: parameters.memory_mib.unwrap_or(0), // 0 means use the default
             numCpus: parameters.cpus.map_or(1, NonZeroU32::get) as i32,
-            cpuAffinity: parameters.cpu_set.clone(),
             taskProfiles: parameters.task_profiles.clone(),
         });
 
diff --git a/compos/composd_cmd/composd_cmd.rs b/compos/composd_cmd/composd_cmd.rs
index d5feed8..b6d82aa 100644
--- a/compos/composd_cmd/composd_cmd.rs
+++ b/compos/composd_cmd/composd_cmd.rs
@@ -31,30 +31,32 @@
     },
 };
 use anyhow::{bail, Context, Result};
+use clap::Parser;
 use compos_common::timeouts::TIMEOUTS;
 use std::sync::{Arc, Condvar, Mutex};
 use std::time::Duration;
 
+#[derive(Parser)]
+enum Actions {
+    /// Compile classpath for real. Output can be used after a reboot.
+    StagedApexCompile {},
+
+    /// Compile classpath in a debugging VM. Output is ignored.
+    TestCompile {
+        /// If any APEX is staged, prefer the staged version.
+        #[clap(long)]
+        prefer_staged: bool,
+    },
+}
+
 fn main() -> Result<()> {
-    #[rustfmt::skip]
-    let app = clap::App::new("composd_cmd")
-        .subcommand(
-            clap::SubCommand::with_name("staged-apex-compile"))
-        .subcommand(
-            clap::SubCommand::with_name("test-compile")
-                .arg(clap::Arg::with_name("prefer-staged").long("prefer-staged")),
-        );
-    let args = app.get_matches();
+    let action = Actions::parse();
 
     ProcessState::start_thread_pool();
 
-    match args.subcommand() {
-        Some(("staged-apex-compile", _)) => run_staged_apex_compile()?,
-        Some(("test-compile", sub_matches)) => {
-            let prefer_staged = sub_matches.is_present("prefer-staged");
-            run_test_compile(prefer_staged)?;
-        }
-        _ => panic!("Unrecognized subcommand"),
+    match action {
+        Actions::StagedApexCompile {} => run_staged_apex_compile()?,
+        Actions::TestCompile { prefer_staged } => run_test_compile(prefer_staged)?,
     }
 
     println!("All Ok!");
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 2ece8f5..3abdc74 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -20,6 +20,7 @@
 use android_logger::LogId;
 use anyhow::{bail, Context, Result};
 use binder::ProcessState;
+use clap::{Parser, ValueEnum};
 use compos_common::compos_client::{ComposClient, VmParameters};
 use compos_common::odrefresh::{
     CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR, PENDING_ARTIFACTS_SUBDIR,
@@ -37,6 +38,24 @@
 
 const MAX_FILE_SIZE_BYTES: u64 = 100 * 1024;
 
+#[derive(Parser)]
+struct Args {
+    /// Type of the VM instance
+    #[clap(long, value_enum)]
+    instance: Instance,
+
+    /// Starts the VM in debug mode
+    #[clap(long, action)]
+    debug: bool,
+}
+
+#[derive(ValueEnum, Clone)]
+enum Instance {
+    Current,
+    Pending,
+    Test,
+}
+
 fn main() {
     android_logger::init_once(
         android_logger::Config::default()
@@ -57,23 +76,11 @@
 }
 
 fn try_main() -> Result<()> {
-    let matches = clap::App::new("compos_verify")
-        .arg(
-            clap::Arg::with_name("instance")
-                .long("instance")
-                .takes_value(true)
-                .required(true)
-                .possible_values(&["current", "pending", "test"]),
-        )
-        .arg(clap::Arg::with_name("debug").long("debug"))
-        .get_matches();
-
-    let debug_mode = matches.is_present("debug");
-    let (instance_dir, artifacts_dir) = match matches.value_of("instance").unwrap() {
-        "current" => (CURRENT_INSTANCE_DIR, CURRENT_ARTIFACTS_SUBDIR),
-        "pending" => (CURRENT_INSTANCE_DIR, PENDING_ARTIFACTS_SUBDIR),
-        "test" => (TEST_INSTANCE_DIR, TEST_ARTIFACTS_SUBDIR),
-        _ => unreachable!("Unexpected instance name"),
+    let args = Args::parse();
+    let (instance_dir, artifacts_dir) = match args.instance {
+        Instance::Current => (CURRENT_INSTANCE_DIR, CURRENT_ARTIFACTS_SUBDIR),
+        Instance::Pending => (CURRENT_INSTANCE_DIR, PENDING_ARTIFACTS_SUBDIR),
+        Instance::Test => (TEST_INSTANCE_DIR, TEST_ARTIFACTS_SUBDIR),
     };
 
     let instance_dir = Path::new(COMPOS_DATA_ROOT).join(instance_dir);
@@ -104,7 +111,7 @@
         instance_image,
         &idsig,
         &idsig_manifest_apk,
-        &VmParameters { debug_mode, ..Default::default() },
+        &VmParameters { debug_mode: args.debug, ..Default::default() },
     )?;
 
     let service = vm_instance.connect_service()?;
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index b7c7a88..4ecd942 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -37,7 +37,6 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
-import java.util.regex.Pattern;
 
 /**
  * Represents a configuration of a virtual machine. A configuration consists of hardware
@@ -57,7 +56,6 @@
     private static final String KEY_PROTECTED_VM = "protectedVm";
     private static final String KEY_MEMORY_MIB = "memoryMib";
     private static final String KEY_NUM_CPUS = "numCpus";
-    private static final String KEY_CPU_AFFINITY = "cpuAffinity";
 
     // Paths to the APK file of this application.
     @NonNull private final String mApkPath;
@@ -106,13 +104,6 @@
     private final int mNumCpus;
 
     /**
-     * Comma-separated list of CPUs or CPU ranges to run vCPUs on (e.g. 0,1-3,5), or
-     * colon-separated list of assignments of vCPU to host CPU assignments (e.g. 0=0:1=1:2=2).
-     * Default is no mask which means a vCPU can run on any host CPU.
-     */
-    private final String mCpuAffinity;
-
-    /**
      * Path within the APK to the payload config file that defines software aspects of this config.
      */
     @NonNull private final String mPayloadConfigPath;
@@ -124,8 +115,7 @@
             DebugLevel debugLevel,
             boolean protectedVm,
             int memoryMib,
-            int numCpus,
-            String cpuAffinity) {
+            int numCpus) {
         mApkPath = apkPath;
         mCerts = certs;
         mPayloadConfigPath = payloadConfigPath;
@@ -133,7 +123,6 @@
         mProtectedVm = protectedVm;
         mMemoryMib = memoryMib;
         mNumCpus = numCpus;
-        mCpuAffinity = cpuAffinity;
     }
 
     /** Loads a config from a stream, for example a file. */
@@ -166,9 +155,8 @@
         final boolean protectedVm = b.getBoolean(KEY_PROTECTED_VM);
         final int memoryMib = b.getInt(KEY_MEMORY_MIB);
         final int numCpus = b.getInt(KEY_NUM_CPUS);
-        final String cpuAffinity = b.getString(KEY_CPU_AFFINITY);
         return new VirtualMachineConfig(apkPath, certs, payloadConfigPath, debugLevel, protectedVm,
-                memoryMib, numCpus, cpuAffinity);
+                memoryMib, numCpus);
     }
 
     /** Persists this config to a stream, for example a file. */
@@ -249,7 +237,6 @@
         parcel.protectedVm = mProtectedVm;
         parcel.memoryMib = mMemoryMib;
         parcel.numCpus = mNumCpus;
-        parcel.cpuAffinity = mCpuAffinity;
         // Don't allow apps to set task profiles ... at last for now. Also, don't forget to
         // validate the string because these are appended to the cmdline argument.
         parcel.taskProfiles = new String[0];
@@ -268,7 +255,6 @@
         private boolean mProtectedVm;
         private int mMemoryMib;
         private int mNumCpus;
-        private String mCpuAffinity;
 
         /**
          * Creates a builder for the given context (APK), and the payload config file in APK.
@@ -281,7 +267,6 @@
             mDebugLevel = DebugLevel.NONE;
             mProtectedVm = false;
             mNumCpus = 1;
-            mCpuAffinity = null;
         }
 
         /**
@@ -326,19 +311,6 @@
         }
 
         /**
-         * Sets on which host CPUs the vCPUs can run. The format is a comma-separated list of CPUs
-         * or CPU ranges to run vCPUs on. e.g. "0,1-3,5" to choose host CPUs 0, 1, 2, 3, and 5.
-         * Or this can be a colon-separated list of assignments of vCPU to host CPU assignments.
-         * e.g. "0=0:1=1:2=2" to map vCPU 0 to host CPU 0, and so on.
-         *
-         * @hide
-         */
-        public Builder cpuAffinity(String affinity) {
-            mCpuAffinity = affinity;
-            return this;
-        }
-
-        /**
          * Builds an immutable {@link VirtualMachineConfig}
          *
          * @hide
@@ -365,13 +337,6 @@
                         + "range [1, " + availableCpus + "]");
             }
 
-            if (mCpuAffinity != null
-                    && !Pattern.matches("[\\d]+(-[\\d]+)?(,[\\d]+(-[\\d]+)?)*", mCpuAffinity)
-                    && !Pattern.matches("[\\d]+=[\\d]+(:[\\d]+=[\\d]+)*", mCpuAffinity)) {
-                throw new IllegalArgumentException("CPU affinity [" + mCpuAffinity + "]"
-                        + " is invalid");
-            }
-
             if (mProtectedVm
                     && !HypervisorProperties.hypervisor_protected_vm_supported().orElse(false)) {
                 throw new UnsupportedOperationException(
@@ -384,7 +349,7 @@
 
             return new VirtualMachineConfig(
                     apkPath, certs, mPayloadConfigPath, mDebugLevel, mProtectedVm, mMemoryMib,
-                    mNumCpus, mCpuAffinity);
+                    mNumCpus);
         }
     }
 }
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
index a9d0da3..d8e7069 100644
--- a/microdroid/initrd/Android.bp
+++ b/microdroid/initrd/Android.bp
@@ -7,7 +7,7 @@
     srcs: ["src/main.rs"],
     rustlibs: [
         "libanyhow",
-        "libstructopt",
+        "libclap",
     ],
     prefer_rlib: true,
 }
diff --git a/microdroid/initrd/src/main.rs b/microdroid/initrd/src/main.rs
index 1023a40..69c6ae4 100644
--- a/microdroid/initrd/src/main.rs
+++ b/microdroid/initrd/src/main.rs
@@ -14,26 +14,23 @@
 
 //! Append bootconfig to initrd image
 use anyhow::Result;
-
+use clap::Parser;
 use std::fs::File;
 use std::io::{Read, Write};
 use std::path::PathBuf;
-use structopt::StructOpt;
 
 const FOOTER_ALIGNMENT: usize = 4;
 const ZEROS: [u8; 4] = [0u8; 4_usize];
 
-#[derive(StructOpt, Debug)]
+#[derive(Parser, Debug)]
 struct Args {
-    /// Output
-    #[structopt(parse(from_os_str), long = "output")]
-    output: PathBuf,
     /// Initrd (without bootconfig)
-    #[structopt(parse(from_os_str))]
     initrd: PathBuf,
     /// Bootconfig
-    #[structopt(parse(from_os_str))]
     bootconfigs: Vec<PathBuf>,
+    /// Output
+    #[clap(long = "output")]
+    output: PathBuf,
 }
 
 fn get_checksum(file_path: &PathBuf) -> Result<u32> {
@@ -67,7 +64,7 @@
 }
 
 fn try_main() -> Result<()> {
-    let args = Args::from_args_safe()?;
+    let args = Args::parse();
     attach_bootconfig(args.initrd, args.bootconfigs, args.output)?;
     Ok(())
 }
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 8a78861..687ce86 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -63,7 +63,6 @@
         protectedVm: false,
         memoryMib: 300,
         numCpus: 1,
-        cpuAffinity: None,
         platformVersion: "~1.0".to_string(),
         taskProfiles: vec![],
     });
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index c7ee0f2..eeadae1 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -19,8 +19,8 @@
 import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 
 import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.common.truth.TruthJUnit.assume;
 
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import android.platform.test.annotations.RootPermissionTest;
@@ -77,8 +77,10 @@
     @After
     public void tearDown() throws Exception {
         // Set PKVM enable and reboot to prevent previous staged session.
-        setPKVMStatusWithRebootToBootloader(true);
-        rebootFromBootloaderAndWaitBootCompleted();
+        if (!isCuttlefish()) {
+            setPKVMStatusWithRebootToBootloader(true);
+            rebootFromBootloaderAndWaitBootCompleted();
+        }
 
         CommandRunner android = new CommandRunner(getDevice());
 
@@ -118,7 +120,7 @@
 
     @Test
     public void testBootWithAndWithoutCompOS() throws Exception {
-        assume().withMessage("Skip on CF; too slow").that(isCuttlefish()).isFalse();
+        assumeFalse(isCuttlefish());
 
         List<Double> bootWithCompOsTime = new ArrayList<>(ROUND_COUNT);
         List<Double> bootWithoutCompOsTime = new ArrayList<>(ROUND_COUNT);
@@ -152,6 +154,8 @@
     }
 
     private void skipIfPKVMStatusSwitchNotSupported() throws Exception {
+        assumeFalse(isCuttlefish());
+
         if (!getDevice().isStateBootloaderOrFastbootd()) {
             getDevice().rebootIntoBootloader();
         }
@@ -160,7 +164,7 @@
         CommandResult result;
         result = getDevice().executeFastbootCommand("oem", "pkvm", "status");
         rebootFromBootloaderAndWaitBootCompleted();
-        assumeTrue(!result.getStderr().contains("Invalid oem command"));
+        assumeFalse(result.getStderr().contains("Invalid oem command"));
         // Skip the test if running on a build with pkvm_enabler. Disabling pKVM
         // for such builds results in a bootloop.
         assumeTrue(result.getStderr().contains("misc=auto"));
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index cff06d5..7a9e2ea 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -235,11 +235,10 @@
             String configPath,
             boolean debug,
             int memoryMib,
-            Optional<Integer> numCpus,
-            Optional<String> cpuAffinity)
+            Optional<Integer> numCpus)
             throws DeviceNotAvailableException {
         return startMicrodroid(androidDevice, buildInfo, apkName, packageName, null, configPath,
-                debug, memoryMib, numCpus, cpuAffinity);
+                debug, memoryMib, numCpus);
     }
 
     public static String startMicrodroid(
@@ -251,12 +250,11 @@
             String configPath,
             boolean debug,
             int memoryMib,
-            Optional<Integer> numCpus,
-            Optional<String> cpuAffinity)
+            Optional<Integer> numCpus)
             throws DeviceNotAvailableException {
         return startMicrodroid(androidDevice, buildInfo, apkName, null, packageName,
                 extraIdsigPaths, configPath, debug,
-                memoryMib, numCpus, cpuAffinity);
+                memoryMib, numCpus);
     }
 
     private static void forwardFileToLog(CommandRunner android, String path, String tag)
@@ -280,8 +278,7 @@
             String configPath,
             boolean debug,
             int memoryMib,
-            Optional<Integer> numCpus,
-            Optional<String> cpuAffinity)
+            Optional<Integer> numCpus)
             throws DeviceNotAvailableException {
         CommandRunner android = new CommandRunner(androidDevice);
 
@@ -314,7 +311,6 @@
                 "--console " + consolePath,
                 "--mem " + memoryMib,
                 numCpus.isPresent() ? "--cpus " + numCpus.get() : "",
-                cpuAffinity.isPresent() ? "--cpu-affinity " + cpuAffinity.get() : "",
                 debugFlag,
                 apkPath,
                 outApkIdsigPath,
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
index 1beee45..d016a67 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
@@ -86,9 +86,8 @@
     private static final int MIN_MEM_ARM64 = 145;
     private static final int MIN_MEM_X86_64 = 196;
 
-    // Number of vCPUs and their affinity to host CPUs for testing purpose
+    // Number of vCPUs for testing purpose
     private static final int NUM_VCPUS = 3;
-    private static final String CPU_AFFINITY = "0,1,2";
 
     @Rule public TestLogData mTestLogs = new TestLogData();
     @Rule public TestName mTestName = new TestName();
@@ -510,8 +509,7 @@
                         configPath,
                         /* debug */ true,
                         minMemorySize(),
-                        Optional.of(NUM_VCPUS),
-                        Optional.of(CPU_AFFINITY));
+                        Optional.of(NUM_VCPUS));
         // check until microdroid is shut down
         CommandRunner android = new CommandRunner(getDevice());
         android.runWithTimeout(15000, "logcat", "-m", "1", "-e", "'crosvm has exited normally'");
@@ -563,8 +561,7 @@
                         configPath,
                         /* debug */ true,
                         minMemorySize(),
-                        Optional.of(NUM_VCPUS),
-                        Optional.of(CPU_AFFINITY));
+                        Optional.of(NUM_VCPUS));
 
         // Check VmCreationRequested atom and clear the statsd report
         List<StatsLog.EventMetricData> data;
@@ -586,7 +583,6 @@
                 AtomsProto.VmCreationRequested.ConfigType.VIRTUAL_MACHINE_APP_CONFIG,
                 atomVmCreationRequested.getConfigType());
         assertEquals(NUM_VCPUS, atomVmCreationRequested.getNumCpus());
-        assertEquals(CPU_AFFINITY, atomVmCreationRequested.getCpuAffinity());
         assertEquals(minMemorySize(), atomVmCreationRequested.getMemoryMib());
         assertEquals(
                 "com.android.art:com.android.compos:com.android.sdkext",
@@ -639,8 +635,7 @@
                         configPath,
                         /* debug */ true,
                         minMemorySize(),
-                        Optional.of(NUM_VCPUS),
-                        Optional.of(CPU_AFFINITY));
+                        Optional.of(NUM_VCPUS));
         adbConnectToMicrodroid(getDevice(), cid);
         waitForBootComplete();
         // Test writing to /data partition
@@ -765,8 +760,7 @@
                         configPath,
                         /* debug */ true,
                         minMemorySize(),
-                        Optional.of(NUM_VCPUS),
-                        Optional.of(CPU_AFFINITY));
+                        Optional.of(NUM_VCPUS));
         adbConnectToMicrodroid(getDevice(), cid);
         waitForBootComplete();
         rootMicrodroid();
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index a6b228d..cf5398d 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -65,15 +65,6 @@
     int numCpus = 1;
 
     /**
-     * Comma-separated list of CPUs or CPU ranges to run vCPUs on (e.g. 0,1-3,5), or
-     * colon-separated list of assignments of vCPU to host CPU assignments (e.g. 0=0:1=1:2=2).
-     * Default is no mask which means a vCPU can run on any host CPU.
-     *
-     * Note: Using a non-null value requires android.permission.USE_CUSTOM_VIRTUAL_MACHINE.
-     */
-    @nullable String cpuAffinity;
-
-    /**
      * List of task profile names to apply for the VM
      *
      * Note: Specifying a value here requires android.permission.USE_CUSTOM_VIRTUAL_MACHINE.
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index d11de03..993bbb0 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -55,13 +55,6 @@
     int numCpus = 1;
 
     /**
-     * Comma-separated list of CPUs or CPU ranges to run vCPUs on (e.g. 0,1-3,5), or
-     * colon-separated list of assignments of vCPU to host CPU assignments (e.g. 0=0:1=1:2=2).
-     * Default is no mask which means a vCPU can run on any host CPU.
-     */
-    @nullable String cpuAffinity;
-
-    /**
      * A version or range of versions of the virtual platform that this config is compatible with.
      * The format follows SemVer.
      */
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index dcc2d48..1eca9fe 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -459,7 +459,6 @@
             protected: *is_protected,
             memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
             cpus: config.numCpus.try_into().ok().and_then(NonZeroU32::new),
-            cpu_affinity: config.cpuAffinity.clone(),
             task_profiles: config.taskProfiles.clone(),
             console_fd,
             log_fd,
@@ -598,7 +597,7 @@
 ) -> Result<VirtualMachineRawConfig> {
     // Controlling CPUs is reserved for platform apps only, even when using
     // VirtualMachineAppConfig.
-    if config.cpuAffinity.is_some() || !config.taskProfiles.is_empty() {
+    if !config.taskProfiles.is_empty() {
         check_use_custom_virtual_machine()?
     }
 
@@ -631,7 +630,6 @@
     vm_config.name = config.name.clone();
     vm_config.protectedVm = config.protectedVm;
     vm_config.numCpus = config.numCpus;
-    vm_config.cpuAffinity = config.cpuAffinity.clone();
     vm_config.taskProfiles = config.taskProfiles.clone();
 
     // Microdroid requires an additional payload disk image and the bootconfig partition.
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
index 01f3e27..3b29d19 100644
--- a/virtualizationservice/src/atom.rs
+++ b/virtualizationservice/src/atom.rs
@@ -66,7 +66,6 @@
     let vm_identifier;
     let config_type;
     let num_cpus;
-    let cpu_affinity;
     let memory_mib;
     let apexes;
     match config {
@@ -74,7 +73,6 @@
             vm_identifier = &config.name;
             config_type = vm_creation_requested::ConfigType::VirtualMachineAppConfig;
             num_cpus = config.numCpus;
-            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
             memory_mib = config.memoryMib;
 
             let vm_payload_config = get_vm_payload_config(config);
@@ -93,7 +91,6 @@
             vm_identifier = &config.name;
             config_type = vm_creation_requested::ConfigType::VirtualMachineRawConfig;
             num_cpus = config.numCpus;
-            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
             memory_mib = config.memoryMib;
             apexes = String::new();
         }
@@ -108,7 +105,7 @@
         binder_exception_code,
         config_type,
         num_cpus,
-        cpu_affinity: &cpu_affinity,
+        cpu_affinity: "", // deprecated
         memory_mib,
         apexes: &apexes,
         // TODO(seungjaeyoo) Fill information about task_profile
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index c579be2..82a9e78 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -80,7 +80,6 @@
     pub protected: bool,
     pub memory_mib: Option<NonZeroU32>,
     pub cpus: Option<NonZeroU32>,
-    pub cpu_affinity: Option<String>,
     pub task_profiles: Vec<String>,
     pub console_fd: Option<File>,
     pub log_fd: Option<File>,
@@ -457,10 +456,6 @@
         command.arg("--cpus").arg(cpus.to_string());
     }
 
-    if let Some(cpu_affinity) = config.cpu_affinity {
-        command.arg("--cpu-affinity").arg(cpu_affinity);
-    }
-
     if !config.task_profiles.is_empty() {
         command.arg("--task-profiles").arg(config.task_profiles.join(","));
     }
diff --git a/vm/Android.bp b/vm/Android.bp
index eac640e..7b016d4 100644
--- a/vm/Android.bp
+++ b/vm/Android.bp
@@ -12,6 +12,7 @@
         "android.system.virtualizationservice-rust",
         "libanyhow",
         "libbinder_rs",
+        "libclap",
         "libenv_logger",
         "liblibc",
         "liblog_rust",
@@ -19,7 +20,6 @@
         "librustutils",
         "libserde_json",
         "libserde",
-        "libstructopt",
         "libvmconfig",
         "libvmclient",
         "libzip",
diff --git a/vm/src/main.rs b/vm/src/main.rs
index ee0e2e6..0845897 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -24,122 +24,104 @@
 };
 use anyhow::{Context, Error};
 use binder::ProcessState;
+use clap::Parser;
 use create_idsig::command_create_idsig;
 use create_partition::command_create_partition;
 use run::{command_run, command_run_app};
 use rustutils::system_properties;
 use std::path::{Path, PathBuf};
-use structopt::clap::AppSettings;
-use structopt::StructOpt;
 
 #[derive(Debug)]
 struct Idsigs(Vec<PathBuf>);
 
-#[derive(StructOpt)]
-#[structopt(no_version, global_settings = &[AppSettings::DisableVersion])]
+#[derive(Parser)]
 enum Opt {
     /// Run a virtual machine with a config in APK
     RunApp {
-        /// Name of VM
-        #[structopt(long)]
-        name: Option<String>,
-
         /// Path to VM Payload APK
-        #[structopt(parse(from_os_str))]
         apk: PathBuf,
 
         /// Path to idsig of the APK
-        #[structopt(parse(from_os_str))]
         idsig: PathBuf,
 
         /// Path to the instance image. Created if not exists.
-        #[structopt(parse(from_os_str))]
         instance: PathBuf,
 
         /// Path to VM config JSON within APK (e.g. assets/vm_config.json)
         config_path: String,
 
+        /// Name of VM
+        #[clap(long)]
+        name: Option<String>,
+
         /// Detach VM from the terminal and run in the background
-        #[structopt(short, long)]
+        #[clap(short, long)]
         daemonize: bool,
 
         /// Path to file for VM console output.
-        #[structopt(long)]
+        #[clap(long)]
         console: Option<PathBuf>,
 
         /// Path to file for VM log output.
-        #[structopt(long)]
+        #[clap(long)]
         log: Option<PathBuf>,
 
         /// Path to file where ramdump is recorded on kernel panic
-        #[structopt(long)]
+        #[clap(long)]
         ramdump: Option<PathBuf>,
 
         /// Debug level of the VM. Supported values: "none" (default), "app_only", and "full".
-        #[structopt(long, default_value = "none", parse(try_from_str=parse_debug_level))]
+        #[clap(long, default_value = "none", value_parser = parse_debug_level)]
         debug: DebugLevel,
 
         /// Run VM in protected mode.
-        #[structopt(short, long)]
+        #[clap(short, long)]
         protected: bool,
 
         /// Memory size (in MiB) of the VM. If unspecified, defaults to the value of `memory_mib`
         /// in the VM config file.
-        #[structopt(short, long)]
+        #[clap(short, long)]
         mem: Option<u32>,
 
         /// Number of vCPUs in the VM. If unspecified, defaults to 1.
-        #[structopt(long)]
+        #[clap(long)]
         cpus: Option<u32>,
 
-        /// Host CPUs where vCPUs are run on. If unspecified, vCPU runs on any host CPU.
-        #[structopt(long)]
-        cpu_affinity: Option<String>,
-
         /// Comma separated list of task profile names to apply to the VM
-        #[structopt(long)]
+        #[clap(long)]
         task_profiles: Vec<String>,
 
         /// Paths to extra idsig files.
-        #[structopt(long = "extra-idsig")]
+        #[clap(long = "extra-idsig")]
         extra_idsigs: Vec<PathBuf>,
     },
     /// Run a virtual machine
     Run {
-        /// Name of VM
-        #[structopt(long)]
-        name: Option<String>,
-
         /// Path to VM config JSON
-        #[structopt(parse(from_os_str))]
         config: PathBuf,
 
+        /// Name of VM
+        #[clap(long)]
+        name: Option<String>,
+
         /// Detach VM from the terminal and run in the background
-        #[structopt(short, long)]
+        #[clap(short, long)]
         daemonize: bool,
 
         /// Number of vCPUs in the VM. If unspecified, defaults to 1.
-        #[structopt(long)]
+        #[clap(long)]
         cpus: Option<u32>,
 
-        /// Host CPUs where vCPUs are run on. If unspecified, vCPU runs on any host CPU. The format
-        /// can be either a comma-separated list of CPUs or CPU ranges to run vCPUs on (e.g.
-        /// "0,1-3,5" to choose host CPUs 0, 1, 2, 3, and 5, or a colon-separated list of
-        /// assignments of vCPU-to-host-CPU assignments e.g. "0=0:1=1:2=2" to map vCPU 0 to host
-        /// CPU 0 and so on.
-        #[structopt(long)]
-        cpu_affinity: Option<String>,
-
         /// Comma separated list of task profile names to apply to the VM
-        #[structopt(long)]
+        #[clap(long)]
         task_profiles: Vec<String>,
 
         /// Path to file for VM console output.
-        #[structopt(long)]
+        #[clap(long)]
         console: Option<PathBuf>,
 
         /// Path to file for VM log output.
-        #[structopt(long)]
+        #[clap(long)]
         log: Option<PathBuf>,
     },
     /// Stop a virtual machine running in the background
@@ -154,23 +136,22 @@
     /// Create a new empty partition to be used as a writable partition for a VM
     CreatePartition {
         /// Path at which to create the image file
-        #[structopt(parse(from_os_str))]
         path: PathBuf,
 
         /// The desired size of the partition, in bytes.
         size: u64,
 
         /// Type of the partition
-        #[structopt(short="t", long="type", default_value="raw", parse(try_from_str=parse_partition_type))]
+        #[clap(short = 't', long = "type", default_value = "raw",
+               value_parser = parse_partition_type)]
         partition_type: PartitionType,
     },
     /// Creates or update the idsig file by digesting the input APK file.
     CreateIdsig {
         /// Path to VM Payload APK
-        #[structopt(parse(from_os_str))]
         apk: PathBuf,
+
         /// Path to idsig of the APK
-        #[structopt(parse(from_os_str))]
         path: PathBuf,
     },
 }
@@ -194,7 +175,7 @@
 
 fn main() -> Result<(), Error> {
     env_logger::init();
-    let opt = Opt::from_args();
+    let opt = Opt::parse();
 
     // We need to start the thread pool for Binder to work properly, especially link_to_death.
     ProcessState::start_thread_pool();
@@ -216,7 +197,6 @@
             protected,
             mem,
             cpus,
-            cpu_affinity,
             task_profiles,
             extra_idsigs,
         } => command_run_app(
@@ -234,11 +214,10 @@
             protected,
             mem,
             cpus,
-            cpu_affinity,
             task_profiles,
             &extra_idsigs,
         ),
-        Opt::Run { name, config, daemonize, cpus, cpu_affinity, task_profiles, console, log } => {
+        Opt::Run { name, config, daemonize, cpus, task_profiles, console, log } => {
             command_run(
                 name,
                 service.as_ref(),
@@ -248,7 +227,6 @@
                 log.as_deref(),
                 /* mem */ None,
                 cpus,
-                cpu_affinity,
                 task_profiles,
             )
         }
diff --git a/vm/src/run.rs b/vm/src/run.rs
index aaa3988..44e15f9 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -49,7 +49,6 @@
     protected: bool,
     mem: Option<u32>,
     cpus: Option<u32>,
-    cpu_affinity: Option<String>,
     task_profiles: Vec<String>,
     extra_idsigs: &[PathBuf],
 ) -> Result<(), Error> {
@@ -102,7 +101,6 @@
         protectedVm: protected,
         memoryMib: mem.unwrap_or(0) as i32, // 0 means use the VM default
         numCpus: cpus.unwrap_or(1) as i32,
-        cpuAffinity: cpu_affinity,
         taskProfiles: task_profiles,
     });
     run(
@@ -127,7 +125,6 @@
     log_path: Option<&Path>,
     mem: Option<u32>,
     cpus: Option<u32>,
-    cpu_affinity: Option<String>,
     task_profiles: Vec<String>,
 ) -> Result<(), Error> {
     let config_file = File::open(config_path).context("Failed to open config file")?;
@@ -144,7 +141,6 @@
     } else {
         config.name = String::from("VmRun");
     }
-    config.cpuAffinity = cpu_affinity;
     config.taskProfiles = task_profiles;
     run(
         service,
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index 58fffff..85e0213 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -59,7 +59,6 @@
         protectedVm: false,
         memoryMib: 300,
         numCpus: 1,
-        cpuAffinity: None,
         platformVersion: "~1.0".to_string(),
         taskProfiles: vec![],
     });