Snap for 6435660 from 2f31feb3b53f23a573b94921701a3147ab1bc055 to sdk-release

Change-Id: Ia96402481fe762ceeda30ee2d09392e5e8d929e0
diff --git a/guest/hals/hwcomposer/cutf_cvm/vsocket_screen_view.cpp b/guest/hals/hwcomposer/cutf_cvm/vsocket_screen_view.cpp
index b67544c..16185b5 100644
--- a/guest/hals/hwcomposer/cutf_cvm/vsocket_screen_view.cpp
+++ b/guest/hals/hwcomposer/cutf_cvm/vsocket_screen_view.cpp
@@ -106,6 +106,10 @@
     auto buff = static_cast<char*>(GetBuffer(current_offset));
     while (size > 0) {
       auto written = screen_server_->Write(buff, size);
+      if (written == -1) {
+        ALOGE("Broadcaster thread failed to write frame: %s", strerror(errno));
+        break;
+      }
       size -= written;
       buff += written;
     }
diff --git a/guest/monitoring/cuttlefish_service/Android.mk b/guest/monitoring/cuttlefish_service/Android.mk
index 2540ef5..b08adaf 100644
--- a/guest/monitoring/cuttlefish_service/Android.mk
+++ b/guest/monitoring/cuttlefish_service/Android.mk
@@ -15,12 +15,12 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_CERTIFICATE := platform
 LOCAL_MODULE_TAGS := optional
 LOCAL_SRC_FILES := $(call all-java-files-under, java)
 LOCAL_STATIC_JAVA_LIBRARIES := guava
 LOCAL_PACKAGE_NAME := CuttlefishService
 LOCAL_SDK_VERSION := 28
+LOCAL_PRIVILEGED_MODULE := true
 LOCAL_PROGUARD_FLAGS := -include build/core/proguard.flags
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_PROGUARD_ENABLED := obfuscation
diff --git a/guest/monitoring/cuttlefish_service/AndroidManifest.xml b/guest/monitoring/cuttlefish_service/AndroidManifest.xml
index 85abd09..428445a 100644
--- a/guest/monitoring/cuttlefish_service/AndroidManifest.xml
+++ b/guest/monitoring/cuttlefish_service/AndroidManifest.xml
@@ -1,21 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.google.gce.gceservice"
-    android:sharedUserId="android.uid.system">
+    package="com.android.google.gce.gceservice">
 
     <uses-sdk android:minSdkVersion="5" />
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
-    <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
-    <uses-permission android:name="android.permission.DUMP" />
 
     <application
         android:label="GceService"
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceBroadcastReceiver.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceBroadcastReceiver.java
index 754eb8f..7b8d864 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceBroadcastReceiver.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceBroadcastReceiver.java
@@ -20,10 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.util.Log;
 
-import com.android.google.gce.gceservice.GceService;
 
 public class GceBroadcastReceiver extends BroadcastReceiver {
     private static final String LOG_TAG = "GceBroadcastReceiver";
@@ -32,7 +30,7 @@
     private void reportIntent(Context context, String intentType) {
         Intent intent = new Intent(context, GceService.class);
         intent.setAction(intentType);
-        context.startService(intent);
+        context.startForegroundService(intent);
     }
 
 
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java
index b070377..34f3b1a 100644
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java
+++ b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/GceService.java
@@ -15,13 +15,16 @@
  */
 package com.android.google.gce.gceservice;
 
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Service;
 import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.util.Log;
-import android.os.Binder;
 import android.os.IBinder;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -37,11 +40,12 @@
     public static final String INTENT_ACTION_CONFIGURE = "com.android.google.gce.gceservice.CONFIGURE";
     public static final String INTENT_ACTION_NETWORK_CHANGED = "com.android.google.gce.gceservice.NETWORK_CHANGED";
     public static final String INTENT_ACTION_BLUETOOTH_CHANGED = "com.android.google.gce.gceservice.BLUETOOTH_CHANGED";
-    private static final int NETWORK_OR_BOOT_TIMEOUT = 30;
+    private static final String NOTIFICATION_CHANNEL_ID = "cuttlefish-service";
+    private static final String NOTIFICATION_CHANNEL_NAME = "Cuttlefish Service";
+    private static final int NOTIFICATION_ID = 1;
 
     private final JobExecutor mExecutor = new JobExecutor();
     private final LocationServicesManager mLocationServices = new LocationServicesManager(this);
-    private final PackageVerifierManager mPackageVerifier = new PackageVerifierManager(this);
     private final PackageVerificationConsentEnforcer mConsentEnforcer = new PackageVerificationConsentEnforcer(this);
     private final BootReporter mBootReporter = new BootReporter();
     private final GceBroadcastReceiver mBroadcastReceiver = new GceBroadcastReceiver();
@@ -66,7 +70,6 @@
             mWifiManager = new GceWifiManager(this, mBootReporter, mExecutor);
 
             mExecutor.schedule(mLocationServices);
-            mExecutor.schedule(mPackageVerifier);
             mExecutor.schedule(mConsentEnforcer);
             mExecutor.schedule(mWifiManager);
             mExecutor.schedule(mBluetoothChecker);
@@ -77,10 +80,19 @@
 
             mExecutor.schedule(mBootReporter,
                     mLocationServices.getLocationServicesReady(),
-                    mPackageVerifier.getPackageVerifierReady(),
                     mBluetoothChecker.getEnabled()
                     // mTombstoneChecker.getTombstoneResult()
                     );
+
+            NotificationManager notificationManager =
+                    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+            NotificationChannel channel =
+                    new NotificationChannel(
+                            NOTIFICATION_CHANNEL_ID,
+                            NOTIFICATION_CHANNEL_NAME,
+                            NotificationManager.IMPORTANCE_LOW);
+            notificationManager.createNotificationChannel(channel);
+
         } catch (Exception e) {
             Log.e(LOG_TAG, "Exception caught", e);
         }
@@ -120,6 +132,15 @@
             Log.e(LOG_TAG, "Missing intent action.");
         }
 
+        Notification notification =
+                new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+                        .setAutoCancel(true)
+                        .setContentTitle("Cuttlefish service is running.")
+                        .setSmallIcon(android.R.drawable.ic_dialog_info)
+                        .setTimeoutAfter(10000)
+                        .build();
+        startForeground(NOTIFICATION_ID, notification);
+
         if (INTENT_ACTION_CONFIGURE.equals(mMostRecentAction)) {
             mExecutor.schedule(mConnChecker);
         } else if (INTENT_ACTION_NETWORK_CHANGED.equals(mMostRecentAction)) {
@@ -128,10 +149,17 @@
             mExecutor.schedule(mBluetoothChecker);
         }
 
+        stopForeground(Service.STOP_FOREGROUND_DETACH);
+
         /* If anything goes wrong, make sure we receive intent again. */
         return Service.START_STICKY;
     }
 
+    @Override
+    public void onDestroy() {
+        unregisterReceiver(mBroadcastReceiver);
+    }
+
     /** Dump the virtual device state
      */
     @Override
@@ -145,8 +173,6 @@
         pw.println("Current system service state:");
         pw.println("  Location service ready: "
             + mLocationServices.getLocationServicesReady().isDone());
-        pw.println("  Package verifier ready: "
-            + mPackageVerifier.getPackageVerifierReady().isDone());
         pw.println("  Network connected: " + mConnChecker.getConnected().isDone());
         pw.println("  WiFi configured: " + mWifiManager.getWifiReady().isDone());
         pw.println("  Bluetooth enabled: " + mBluetoothChecker.getEnabled().isDone());
diff --git a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/PackageVerifierManager.java b/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/PackageVerifierManager.java
deleted file mode 100644
index e10845a..0000000
--- a/guest/monitoring/cuttlefish_service/java/com/android/google/gce/gceservice/PackageVerifierManager.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 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.google.gce.gceservice;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.util.Log;
-
-/**
- * Disable package verifier.
- */
-public class PackageVerifierManager extends JobBase {
-    private static final String LOG_TAG = "GcePackageVerifierManager";
-    private static final String SETTING_PACKAGE_VERIFIER_ENABLE = "verifier_verify_adb_installs";
-    private final Context mContext;
-    private final GceFuture<Boolean> mResult =
-            new GceFuture<Boolean>("Package Verifier");
-
-
-    PackageVerifierManager(Context context) {
-        super(LOG_TAG);
-        mContext = context;
-    }
-
-
-    private boolean getAndLogPackageVerifierState() {
-        int package_verifier_state = 1;
-        try {
-            ContentResolver contentResolver = mContext.getContentResolver();
-            package_verifier_state = Settings.Secure.getInt(contentResolver, SETTING_PACKAGE_VERIFIER_ENABLE);
-        } catch (SettingNotFoundException e) {
-            Log.w(LOG_TAG, "Could not read package verifier state. Assuming it's enabled.");
-        }
-
-        return package_verifier_state != 0;
-    }
-
-
-    public int execute() {
-        if (getAndLogPackageVerifierState()) {
-            Settings.Secure.putInt(mContext.getContentResolver(), SETTING_PACKAGE_VERIFIER_ENABLE, 0);
-            // One more call, just to log the state.
-            getAndLogPackageVerifierState();
-        }
-
-        mResult.set(true);
-        return 0;
-    }
-
-
-    public void onDependencyFailed(Exception e) {
-        Log.e(LOG_TAG, "Could not disable Package Verifier.", e);
-        mResult.set(e);
-    }
-
-
-    public GceFuture<Boolean> getPackageVerifierReady() {
-        return mResult;
-    }
-}
diff --git a/host/commands/assemble_cvd/assembler_defs.h b/host/commands/assemble_cvd/assembler_defs.h
index b890ca0..233f2f5 100644
--- a/host/commands/assemble_cvd/assembler_defs.h
+++ b/host/commands/assemble_cvd/assembler_defs.h
@@ -17,9 +17,6 @@
 
 namespace cvd {
 
-constexpr char kLogcatSerialMode[] = "serial";
-constexpr char kLogcatVsockMode[] = "vsock";
-
 enum AssemblerExitCodes : int {
   kSuccess = 0,
   kArgumentParsingError = 1,
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 103ff69..6df46ef 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -435,7 +435,20 @@
     instance.set_vnc_server_port(6444 + num - 1);
     instance.set_host_port(6520 + num - 1);
     instance.set_adb_ip_and_port("127.0.0.1:" + std::to_string(6520 + num - 1));
-    instance.set_tpm_port(2321 + num - 1);
+    instance.set_tpm_port(2321 + (num * 2) - 2);
+    instance.set_tombstone_receiver_port(6600 + num - 1);
+    instance.set_logcat_port(6700 + num - 1);
+    instance.set_config_server_port(6800 + num - 1);
+
+    if (FLAGS_gpu_mode != vsoc::kGpuModeDrmVirgl &&
+        FLAGS_gpu_mode != vsoc::kGpuModeGfxStream) {
+      instance.set_frames_server_port(6900 + num - 1);
+    }
+
+    if (FLAGS_vm_manager == vm_manager::QemuManager::name()) {
+      instance.set_keyboard_server_port(7000 + num - 1);
+      instance.set_touch_server_port(7100 + num - 1);
+    }
 
     instance.set_device_title(FLAGS_device_title);
 
@@ -945,7 +958,7 @@
 
   for (const auto& instance : config->Instances()) {
     if (!cvd::FileExists(instance.access_kregistry_path())) {
-      CreateBlankImage(instance.access_kregistry_path(), 1, "none", "64K");
+      CreateBlankImage(instance.access_kregistry_path(), 2, "none", "1M");
     }
   }
 
@@ -975,7 +988,7 @@
                      << "newer than its underlying composite disk. Wiping the overlay.";
       }
       CreateQcowOverlay(config->crosvm_binary(), config->composite_disk_path(), overlay_path);
-      CreateBlankImage(instance.access_kregistry_path(), 1, "none", "64K");
+      CreateBlankImage(instance.access_kregistry_path(), 2, "none", "1M");
     }
   }
 
diff --git a/host/commands/assemble_cvd/image_aggregator.cc b/host/commands/assemble_cvd/image_aggregator.cc
index 6844ff3..04ec6b3 100644
--- a/host/commands/assemble_cvd/image_aggregator.cc
+++ b/host/commands/assemble_cvd/image_aggregator.cc
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+/*
+ * GUID Partition Table and Composite Disk generation code.
+ */
+
 #include "host/commands/assemble_cvd/image_aggregator.h"
 
 #include <sys/types.h>
@@ -61,6 +65,12 @@
 
 static_assert(sizeof(MasterBootRecord) == SECTOR_SIZE);
 
+/**
+ * Creates a "Protective" Master Boot Record Partition Table header. The GUID
+ * Partition Table Specification recommends putting this on the first sector
+ * of the disk, to protect against old disk formatting tools from misidentifying
+ * the GUID Partition Table later and doing the wrong thing.
+ */
 MasterBootRecord ProtectiveMbr(std::uint64_t size) {
   MasterBootRecord mbr = {
     .partitions =  {{
@@ -127,6 +137,17 @@
   std::uint64_t offset;
 };
 
+/*
+ * Returns the file size of `file_path`. If `file_path` is an Android-Sparse
+ * file, returns the file size it would have after being converted to a raw
+ * file.
+ *
+ * Android-Sparse is a file format invented by Android that optimizes for
+ * chunks of zeroes or repeated data. The Android build system can produce
+ * sparse files to save on size of disk files after they are extracted from a
+ * disk file, as the imag eflashing process also can handle Android-Sparse
+ * images.
+ */
 std::uint64_t UnsparsedSize(const std::string& file_path) {
   auto fd = open(file_path.c_str(), O_RDONLY);
   CHECK(fd >= 0) << "Could not open \"" << file_path << "\""
@@ -138,6 +159,9 @@
   return size;
 }
 
+/*
+ * strncpy equivalent for u16 data. GPT disks use UTF16-LE for disk labels.
+ */
 void u16cpy(std::uint16_t* dest, std::uint16_t* src, std::size_t size) {
   while (size > 0 && *src) {
     *dest = *src;
@@ -150,6 +174,10 @@
   }
 }
 
+/**
+ * Incremental builder class for producing partition tables. Add partitions
+ * one-by-one, then produce specification files
+ */
 class CompositeDiskBuilder {
 private:
   std::vector<PartitionInfo> partitions_;
@@ -167,6 +195,11 @@
     next_disk_offset_ += size;
   }
 
+  /**
+   * Generates a composite disk specification file, assuming that `header_file`
+   * and `footer_file` will be populated with the contents of `Beginning()` and
+   * `End()`.
+   */
   CompositeDisk MakeCompositeDiskSpec(const std::string& header_file,
                                       const std::string& footer_file) const {
     CompositeDisk disk;
@@ -191,6 +224,13 @@
     return disk;
   }
 
+  /*
+   * Returns a GUID Partition Table header structure for all the disks that have
+   * been added with `AppendDisk`. Includes a protective Master Boot Record.
+   *
+   * This method is not deterministic: some data is generated such as the disk
+   * uuids.
+   */
   GptBeginning Beginning() const {
     if (partitions_.size() > GPT_NUM_PARTITIONS) {
       LOG(FATAL) << "Too many partitions: " << partitions_.size();
@@ -216,7 +256,8 @@
       const auto& partition = partitions_[i];
       gpt.entries[i] = GptPartitionEntry {
         .first_lba = partition.offset / SECTOR_SIZE,
-        .last_lba = (partition.offset + partition.size - SECTOR_SIZE) / SECTOR_SIZE,
+        .last_lba = (partition.offset + partition.size - SECTOR_SIZE)
+                    / SECTOR_SIZE,
       };
       uuid_generate(gpt.entries[i].unique_partition_guid);
       // The right uuid is technically 0FC63DAF-8483-4772-8E79-3D69D8477DE4.
@@ -240,6 +281,9 @@
     return gpt;
   }
 
+  /**
+   * Generates a GUID Partition Table footer that matches the header in `head`.
+   */
   GptEnd End(const GptBeginning& head) const {
     GptEnd gpt;
     std::memcpy((void*) gpt.entries, (void*) head.entries, 128 * 128);
@@ -271,6 +315,18 @@
   return true;
 }
 
+/**
+ * Converts any Android-Sparse image files in `partitions` to raw image files.
+ *
+ * Android-Sparse is a file format invented by Android that optimizes for
+ * chunks of zeroes or repeated data. The Android build system can produce
+ * sparse files to save on size of disk files after they are extracted from a
+ * disk file, as the imag eflashing process also can handle Android-Sparse
+ * images.
+ *
+ * crosvm has read-only support for Android-Sparse files, but QEMU does not
+ * support them.
+ */
 void DeAndroidSparse(const std::vector<ImagePartition>& partitions) {
   for (const auto& partition : partitions) {
     auto fd = open(partition.image_file_path.c_str(), O_RDONLY);
@@ -293,7 +349,8 @@
     int write_status = sparse_file_write(sparse, write_fd, /* gz */ false,
                                          /* sparse */ false, /* crc */ false);
     if (write_status < 0) {
-      LOG(FATAL) << "Failed to desparse \"" << partition.image_file_path << "\": " << write_status;
+      LOG(FATAL) << "Failed to desparse \"" << partition.image_file_path
+                 << "\": " << write_status;
     }
     close(write_fd);
     if (rename(out_file_name.c_str(), partition.image_file_path.c_str()) < 0) {
diff --git a/host/commands/assemble_cvd/image_aggregator.h b/host/commands/assemble_cvd/image_aggregator.h
index d27931b..23581f9 100644
--- a/host/commands/assemble_cvd/image_aggregator.h
+++ b/host/commands/assemble_cvd/image_aggregator.h
@@ -13,9 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #pragma once
 
+/**
+ * Functions for manipulating disk files given to crosvm or QEMU.
+ */
+
 #include <string>
 #include <vector>
 
@@ -24,12 +27,44 @@
   std::string image_file_path;
 };
 
+/**
+ * Combine the files in `partition` into a single raw disk file and write it to
+ * `output_path`. The raw disk file will have a GUID Partition Table and copy in
+ * the contents of the files mentioned in `partitions`.
+ */
 void AggregateImage(const std::vector<ImagePartition>& partitions,
                     const std::string& output_path);
+
+/**
+ * Generate the files necessary for booting with a Composite Disk.
+ *
+ * Composite Disk is a crosvm disk format that is a layer of indirection over
+ * other disk files. The Composite Disk file lists names and offsets in the
+ * virtual disk.
+ *
+ * For a complete single disk inside the VM, there must also be a GUID Partition
+ * Table header and footer. These are saved to `header_file` and `footer_file`,
+ * then the specification file containing the file paths and offsets is saved to
+ * `output_composite_path`.
+ */
 void CreateCompositeDisk(std::vector<ImagePartition> partitions,
                          const std::string& header_file,
                          const std::string& footer_file,
                          const std::string& output_composite_path);
+
+/**
+ * Generate a qcow overlay backed by a given implementation file.
+ *
+ * qcow, or "QEMU Copy-On-Write" is a file format containing a list of disk
+ * offsets and file contents. This can be combined with a backing file, to
+ * represent an original disk file plus disk updates over that file. The qcow
+ * files can be swapped out and replaced without affecting the original. qcow
+ * is supported by QEMU and crosvm.
+ *
+ * The crosvm binary at `crosvm_path` is used to generate an overlay file at
+ * `output_overlay_path` that functions as an overlay on the file at
+ * `backing_file`.
+ */
 void CreateQcowOverlay(const std::string& crosvm_path,
                        const std::string& backing_file,
                        const std::string& output_overlay_path);
diff --git a/host/commands/run_cvd/Android.bp b/host/commands/run_cvd/Android.bp
index 2afc59c..b166417 100644
--- a/host/commands/run_cvd/Android.bp
+++ b/host/commands/run_cvd/Android.bp
@@ -16,7 +16,6 @@
 cc_binary_host {
     name: "run_cvd",
     srcs: [
-        "kernel_args.cc",
         "launch.cc",
         "main.cc",
         "process_monitor.cc",
diff --git a/host/commands/run_cvd/kernel_args.cc b/host/commands/run_cvd/kernel_args.cc
deleted file mode 100644
index b17492b..0000000
--- a/host/commands/run_cvd/kernel_args.cc
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#include "host/commands/run_cvd/kernel_args.h"
-
-#include <string>
-#include <vector>
-
-#include "host/commands/run_cvd/launch.h"
-#include "host/commands/run_cvd/runner_defs.h"
-#include "host/libs/config/cuttlefish_config.h"
-#include "host/libs/vm_manager/vm_manager.h"
-
-template<typename T>
-static void AppendVector(std::vector<T>* destination, const std::vector<T>& source) {
-  destination->insert(destination->end(), source.begin(), source.end());
-}
-
-template<typename S, typename T>
-static std::string concat(const S& s, const T& t) {
-  std::ostringstream os;
-  os << s << t;
-  return os.str();
-}
-
-std::vector<std::string> KernelCommandLineFromConfig(const vsoc::CuttlefishConfig& config) {
-  auto instance = config.ForDefaultInstance();
-  std::vector<std::string> kernel_cmdline;
-
-  AppendVector(&kernel_cmdline, config.boot_image_kernel_cmdline());
-  AppendVector(&kernel_cmdline,
-               vm_manager::VmManager::ConfigureGpuMode(config.vm_manager(), config.gpu_mode()));
-  AppendVector(&kernel_cmdline, vm_manager::VmManager::ConfigureBootDevices(config.vm_manager()));
-
-  kernel_cmdline.push_back(concat("androidboot.serialno=", instance.serial_number()));
-  kernel_cmdline.push_back(concat("androidboot.lcd_density=", config.dpi()));
-  if (config.logcat_mode() == cvd::kLogcatVsockMode) {
-  }
-  kernel_cmdline.push_back(concat(
-      "androidboot.setupwizard_mode=", config.setupwizard_mode()));
-  if (!config.use_bootloader()) {
-    std::string slot_suffix;
-    if (config.boot_slot().empty()) {
-      slot_suffix = "_a";
-    } else {
-      slot_suffix = "_" + config.boot_slot();
-    }
-    kernel_cmdline.push_back(concat("androidboot.slot_suffix=", slot_suffix));
-  }
-  kernel_cmdline.push_back(concat("loop.max_part=", config.loop_max_part()));
-  if (config.guest_enforce_security()) {
-    kernel_cmdline.push_back("enforcing=1");
-  } else {
-    kernel_cmdline.push_back("enforcing=0");
-    kernel_cmdline.push_back("androidboot.selinux=permissive");
-  }
-  if (config.guest_audit_security()) {
-    kernel_cmdline.push_back("audit=1");
-  } else {
-    kernel_cmdline.push_back("audit=0");
-  }
-  if (config.guest_force_normal_boot()) {
-    kernel_cmdline.push_back("androidboot.force_normal_boot=1");
-  }
-
-  AppendVector(&kernel_cmdline, config.extra_kernel_cmdline());
-
-  return kernel_cmdline;
-}
-
-std::vector<std::string> KernelCommandLineFromStreamer(
-    const StreamerLaunchResult& streamer_launch) {
-  std::vector<std::string> kernel_args;
-  if (streamer_launch.frames_server_vsock_port) {
-    kernel_args.push_back(concat("androidboot.vsock_frames_port=",
-                                 *streamer_launch.frames_server_vsock_port));
-  }
-  if (streamer_launch.touch_server_vsock_port) {
-    kernel_args.push_back(concat("androidboot.vsock_touch_port=",
-                                 *streamer_launch.touch_server_vsock_port));
-  }
-  if (streamer_launch.keyboard_server_vsock_port) {
-    kernel_args.push_back(concat("androidboot.vsock_keyboard_port=",
-                                 *streamer_launch.keyboard_server_vsock_port));
-  }
-  return kernel_args;
-}
-
-std::vector<std::string> KernelCommandLineFromTombstone(const TombstoneReceiverPorts& tombstone) {
-  if (!tombstone.server_vsock_port) {
-    return { "androidboot.tombstone_transmit=0" };
-  }
-  return {
-    "androidboot.tombstone_transmit=1",
-    concat("androidboot.vsock_tombstone_port=", *tombstone.server_vsock_port),
-  };
-}
-
-std::vector<std::string> KernelCommandLineFromConfigServer(const ConfigServerPorts& config_server) {
-  if (!config_server.server_vsock_port) {
-    return {};
-  }
-  return {
-    concat("androidboot.cuttlefish_config_server_port=", *config_server.server_vsock_port),
-  };
-}
-
-std::vector<std::string> KernelCommandLineFromLogcatServer(const LogcatServerPorts& logcat_server) {
-  if (!logcat_server.server_vsock_port) {
-    return {};
-  }
-  return {
-    concat("androidboot.vsock_logcat_port=", *logcat_server.server_vsock_port),
-  };
-}
-
-std::vector<std::string> KernelCommandLineFromTpm(const TpmPorts& tpm) {
-  if (!tpm.server_vsock_port) {
-    return {};
-  }
-  return {
-    concat("androidboot.tpm_vsock_port=", *tpm.server_vsock_port),
-  };
-}
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index 16b0364..abf1f6f 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -88,11 +88,11 @@
   if (config.vm_manager() == vm_manager::QemuManager::name()) {
     cmd->AddParameter("-write_virtio_input");
 
-    touch_server = cvd::SharedFD::VsockServer(SOCK_STREAM);
-    server_ret.touch_server_vsock_port = touch_server->VsockServerPort();
-
-    keyboard_server = cvd::SharedFD::VsockServer(SOCK_STREAM);
-    server_ret.keyboard_server_vsock_port = keyboard_server->VsockServerPort();
+    touch_server = cvd::SharedFD::VsockServer(instance.touch_server_port(),
+                                              SOCK_STREAM);
+    keyboard_server =
+        cvd::SharedFD::VsockServer(instance.keyboard_server_port(),
+                                   SOCK_STREAM);
   } else {
     touch_server = CreateUnixInputServer(instance.touch_socket_path());
     keyboard_server = CreateUnixInputServer(instance.keyboard_socket_path());
@@ -114,8 +114,8 @@
       config.gpu_mode() == vsoc::kGpuModeGfxStream) {
     frames_server = CreateUnixInputServer(instance.frames_socket_path());
   } else {
-    frames_server = cvd::SharedFD::VsockServer(SOCK_STREAM);
-    server_ret.frames_server_vsock_port = frames_server->VsockServerPort();
+    frames_server = cvd::SharedFD::VsockServer(instance.frames_server_port(),
+                                               SOCK_STREAM);
   }
   if (!frames_server->IsOpen()) {
     LOG(ERROR) << "Could not open frames server: " << frames_server->StrError();
@@ -177,12 +177,14 @@
   return ret;
 }
 
-LogcatServerPorts LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
-                                                cvd::ProcessMonitor* process_monitor) {
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                   cvd::ProcessMonitor* process_monitor) {
   if (!LogcatReceiverEnabled(config)) {
-    return {};
+    return;
   }
-  auto socket = cvd::SharedFD::VsockServer(SOCK_STREAM);
+  auto instance = config.ForDefaultInstance();
+  auto port = instance.logcat_port();
+  auto socket = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
   if (!socket->IsOpen()) {
     LOG(ERROR) << "Unable to create logcat server socket: "
                << socket->StrError();
@@ -192,12 +194,14 @@
   cmd.AddParameter("-server_fd=", socket);
   process_monitor->StartSubprocess(std::move(cmd),
                                    GetOnSubprocessExitCallback(config));
-  return { socket->VsockServerPort() };
+  return;
 }
 
-ConfigServerPorts LaunchConfigServer(const vsoc::CuttlefishConfig& config,
-                                     cvd::ProcessMonitor* process_monitor) {
-  auto socket = cvd::SharedFD::VsockServer(SOCK_STREAM);
+void LaunchConfigServer(const vsoc::CuttlefishConfig& config,
+                        cvd::ProcessMonitor* process_monitor) {
+  auto instance = config.ForDefaultInstance();
+  auto port = instance.config_server_port();
+  auto socket = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
   if (!socket->IsOpen()) {
     LOG(ERROR) << "Unable to create configuration server socket: "
                << socket->StrError();
@@ -207,13 +211,13 @@
   cmd.AddParameter("-server_fd=", socket);
   process_monitor->StartSubprocess(std::move(cmd),
                                    GetOnSubprocessExitCallback(config));
-  return { socket->VsockServerPort() };
+  return;
 }
 
-TombstoneReceiverPorts LaunchTombstoneReceiverIfEnabled(
-    const vsoc::CuttlefishConfig& config, cvd::ProcessMonitor* process_monitor) {
+void LaunchTombstoneReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                      cvd::ProcessMonitor* process_monitor) {
   if (!config.enable_tombstone_receiver()) {
-    return {};
+    return;
   }
   auto instance = config.ForDefaultInstance();
 
@@ -225,16 +229,17 @@
       LOG(ERROR) << "Failed to create tombstone directory: " << tombstoneDir
                  << ". Error: " << errno;
       exit(RunnerExitCodes::kTombstoneDirCreationError);
-      return {};
+      return;
     }
   }
 
-  auto socket = cvd::SharedFD::VsockServer(SOCK_STREAM);
+  auto port = instance.tombstone_receiver_port();
+  auto socket = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
   if (!socket->IsOpen()) {
     LOG(ERROR) << "Unable to create tombstone server socket: "
                << socket->StrError();
     std::exit(RunnerExitCodes::kTombstoneServerError);
-    return {};
+    return;
   }
   cvd::Command cmd(config.tombstone_receiver_binary());
   cmd.AddParameter("-server_fd=", socket);
@@ -242,7 +247,7 @@
 
   process_monitor->StartSubprocess(std::move(cmd),
                                    GetOnSubprocessExitCallback(config));
-  return { socket->VsockServerPort() };
+  return;
 }
 
 StreamerLaunchResult LaunchVNCServer(
@@ -341,9 +346,11 @@
   }
 }
 
-TpmPorts LaunchTpmSimulator(cvd::ProcessMonitor* process_monitor,
-                            const vsoc::CuttlefishConfig& config) {
-  int port = config.ForDefaultInstance().tpm_port();
+void LaunchTpmSimulator(cvd::ProcessMonitor* process_monitor,
+                   const vsoc::CuttlefishConfig& config) {
+  auto instance = config.ForDefaultInstance();
+  auto port = instance.tpm_port();
+  auto socket = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
   cvd::Command tpm_command(
       vsoc::DefaultHostArtifactsPath("bin/tpm_simulator_manager"));
   tpm_command.AddParameter("-port=", port);
@@ -356,7 +363,6 @@
   proxy_command.AddParameter("--vsock_port=", port);
   process_monitor->StartSubprocess(std::move(proxy_command),
                                    GetOnSubprocessExitCallback(config));
-  return TpmPorts{port};
 }
 
 void LaunchMetrics(cvd::ProcessMonitor* process_monitor,
@@ -367,8 +373,8 @@
                                    GetOnSubprocessExitCallback(config));
 }
 
-TpmPorts LaunchTpmPassthrough(cvd::ProcessMonitor* process_monitor,
-                              const vsoc::CuttlefishConfig& config) {
+void LaunchTpmPassthrough(cvd::ProcessMonitor* process_monitor,
+                          const vsoc::CuttlefishConfig& config) {
   auto server = cvd::SharedFD::VsockServer(SOCK_STREAM);
   if (!server->IsOpen()) {
     LOG(ERROR) << "Unable to create tpm passthrough server: "
@@ -382,20 +388,16 @@
 
   process_monitor->StartSubprocess(std::move(tpm_command),
                                    GetOnSubprocessExitCallback(config));
-
-  return TpmPorts{server->VsockServerPort()};
 }
 
-TpmPorts LaunchTpm(cvd::ProcessMonitor* process_monitor,
-                   const vsoc::CuttlefishConfig& config) {
+void LaunchTpm(cvd::ProcessMonitor* process_monitor,
+               const vsoc::CuttlefishConfig& config) {
   if (config.tpm_device() != "") {
     if (config.tpm_binary() != "") {
       LOG(WARNING) << "Both -tpm_device and -tpm_binary were set. Using -tpm_device.";
     }
-    return LaunchTpmPassthrough(process_monitor, config);
+    LaunchTpmPassthrough(process_monitor, config);
   } else if (config.tpm_binary() != "") {
-    return LaunchTpmSimulator(process_monitor, config);
-  } else {
-    return TpmPorts{};
+    LaunchTpmSimulator(process_monitor, config);
   }
 }
diff --git a/host/commands/run_cvd/launch.h b/host/commands/run_cvd/launch.h
index 53c97cd..af3ec40 100644
--- a/host/commands/run_cvd/launch.h
+++ b/host/commands/run_cvd/launch.h
@@ -21,42 +21,24 @@
 
 struct StreamerLaunchResult {
   bool launched = false;
-  std::optional<unsigned int> frames_server_vsock_port;
-  std::optional<unsigned int> touch_server_vsock_port;
-  std::optional<unsigned int> keyboard_server_vsock_port;
 };
 StreamerLaunchResult LaunchVNCServer(
     const vsoc::CuttlefishConfig& config,
     cvd::ProcessMonitor* process_monitor,
     std::function<bool(cvd::MonitorEntry*)> callback);
 
-struct TombstoneReceiverPorts {
-  std::optional<unsigned int> server_vsock_port;
-};
-TombstoneReceiverPorts LaunchTombstoneReceiverIfEnabled(
-    const vsoc::CuttlefishConfig& config, cvd::ProcessMonitor* process_monitor);
-
-struct ConfigServerPorts {
-  std::optional<unsigned int> server_vsock_port;
-};
-ConfigServerPorts LaunchConfigServer(const vsoc::CuttlefishConfig& config,
-                                     cvd::ProcessMonitor* process_monitor);
-
-struct LogcatServerPorts {
-  std::optional<unsigned int> server_vsock_port;
-};
-LogcatServerPorts LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
-                                                cvd::ProcessMonitor* process_monitor);
+void LaunchTombstoneReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                      cvd::ProcessMonitor* process_monitor);
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+                                   cvd::ProcessMonitor* process_monitor);
+void LaunchConfigServer(const vsoc::CuttlefishConfig& config,
+                        cvd::ProcessMonitor* process_monitor);
 
 StreamerLaunchResult LaunchWebRTC(cvd::ProcessMonitor* process_monitor,
                                   const vsoc::CuttlefishConfig& config);
 
-struct TpmPorts {
-  std::optional<unsigned int> server_vsock_port;
-};
-
-TpmPorts LaunchTpm(cvd::ProcessMonitor* process_monitor,
-                   const vsoc::CuttlefishConfig& config);
+void LaunchTpm(cvd::ProcessMonitor* process_monitor,
+               const vsoc::CuttlefishConfig& config);
 
 void LaunchMetrics(cvd::ProcessMonitor* process_monitor,
                                   const vsoc::CuttlefishConfig& config);
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index e96dcc8..c72237e 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -48,11 +48,11 @@
 #include "common/libs/utils/network.h"
 #include "common/libs/utils/subprocess.h"
 #include "common/libs/utils/size_utils.h"
-#include "host/commands/run_cvd/kernel_args.h"
 #include "host/commands/run_cvd/launch.h"
 #include "host/commands/run_cvd/runner_defs.h"
 #include "host/commands/run_cvd/process_monitor.h"
 #include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/kernel_args.h"
 #include "host/commands/kernel_log_monitor/kernel_log_server.h"
 #include <host/libs/vm_manager/crosvm_manager.h>
 #include "host/libs/vm_manager/vm_manager.h"
@@ -417,22 +417,13 @@
   cvd::SharedFD adbd_events_pipe = event_pipes[1];
   event_pipes.clear();
 
-  std::set<std::string> extra_kernel_cmdline;
-
   SetUpHandlingOfBootEvents(&process_monitor, boot_events_pipe,
                             boot_state_machine);
 
-  auto logcat_server = LaunchLogcatReceiverIfEnabled(*config, &process_monitor);
-  auto logcat_server_args = KernelCommandLineFromLogcatServer(logcat_server);
-
-  auto config_server = LaunchConfigServer(*config, &process_monitor);
-  auto config_server_args = KernelCommandLineFromConfigServer(config_server);
-
-  auto tombstone_server = LaunchTombstoneReceiverIfEnabled(*config, &process_monitor);
-  auto tombstone_kernel_args = KernelCommandLineFromTombstone(tombstone_server);
-
-  auto tpm_server = LaunchTpm(&process_monitor, *config);
-  auto tpm_kernel_args = KernelCommandLineFromTpm(tpm_server);
+  LaunchLogcatReceiverIfEnabled(*config, &process_monitor);
+  LaunchConfigServer(*config, &process_monitor);
+  LaunchTombstoneReceiverIfEnabled(*config, &process_monitor);
+  LaunchTpm(&process_monitor, *config);
 
   // The streamer needs to launch before the VMM because it serves on several
   // sockets (input devices, vsock frame server) when using crosvm.
@@ -445,16 +436,7 @@
     streamer_config = LaunchWebRTC(&process_monitor, *config);
   }
 
-  auto streamer_kernel_args = KernelCommandLineFromStreamer(streamer_config);
-
   auto kernel_args = KernelCommandLineFromConfig(*config);
-  kernel_args.insert(kernel_args.end(), streamer_kernel_args.begin(),
-                     streamer_kernel_args.end());
-  kernel_args.insert(kernel_args.end(), tombstone_kernel_args.begin(),
-                     tombstone_kernel_args.end());
-  kernel_args.insert(kernel_args.end(), config_server_args.begin(), config_server_args.end());
-  kernel_args.insert(kernel_args.end(), logcat_server_args.begin(), logcat_server_args.end());
-  kernel_args.insert(kernel_args.end(), tpm_kernel_args.begin(), tpm_kernel_args.end());
 
   // Start the guest VM
   vm_manager->WithFrontend(streamer_config.launched);
diff --git a/host/commands/run_cvd/runner_defs.h b/host/commands/run_cvd/runner_defs.h
index eacc96b..6ee1460 100644
--- a/host/commands/run_cvd/runner_defs.h
+++ b/host/commands/run_cvd/runner_defs.h
@@ -17,9 +17,6 @@
 
 namespace cvd {
 
-constexpr char kLogcatSerialMode[] = "serial";
-constexpr char kLogcatVsockMode[] = "vsock";
-
 enum RunnerExitCodes : int {
   kSuccess = 0,
   kArgumentParsingError = 1,
diff --git a/host/commands/tpm_simulator_manager/tpm_simulator_manager.cpp b/host/commands/tpm_simulator_manager/tpm_simulator_manager.cpp
index ebda947..cd726f3 100644
--- a/host/commands/tpm_simulator_manager/tpm_simulator_manager.cpp
+++ b/host/commands/tpm_simulator_manager/tpm_simulator_manager.cpp
@@ -72,6 +72,8 @@
   bool platform_server = false;
   bool sent_init = false;
 
+  cvd::SharedFD client; // Hold this connection open for the process lifetime.
+
   char* lineptr = nullptr;
   size_t size = 0;
   while (getline(&lineptr, &size, stdout_file.get()) >= 0) {
@@ -84,7 +86,7 @@
       platform_server = true;
     }
     if (command_server && platform_server && !sent_init) {
-      auto client = cvd::SharedFD::SocketLocalClient(FLAGS_port + 1, SOCK_STREAM);
+      client = cvd::SharedFD::SocketLocalClient(FLAGS_port + 1, SOCK_STREAM);
       std::vector<char> command_bytes(4, 0);
       *reinterpret_cast<std::uint32_t*>(command_bytes.data()) = htobe32(1); // TPM_SIGNAL_POWER_ON
       CHECK(cvd::WriteAll(client, command_bytes) == 4) << "Could not send TPM_SIGNAL_POWER_ON";
diff --git a/host/libs/config/Android.bp b/host/libs/config/Android.bp
index 37e6224..f7251e7 100644
--- a/host/libs/config/Android.bp
+++ b/host/libs/config/Android.bp
@@ -18,6 +18,7 @@
     srcs: [
         "cuttlefish_config.cpp",
         "fetcher_config.cpp",
+        "kernel_args.cpp",
     ],
     shared_libs: [
         "libcuttlefish_fs",
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 6b852ef..83428a9 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -143,11 +143,14 @@
 const char* kBlankDataImageFmt = "blank_data_image_fmt";
 
 const char* kLogcatMode = "logcat_mode";
+const char* kLogcatPort = "logcat_port";
 const char* kLogcatReceiverBinary = "logcat_receiver_binary";
+const char* kConfigServerPort = "config_server_port";
 const char* kConfigServerBinary = "config_server_binary";
 
 const char* kRunTombstoneReceiver = "enable_tombstone_logger";
 const char* kTombstoneReceiverBinary = "tombstone_receiver_binary";
+const char* kTombstoneReceiverPort = "tombstone_receiver_port";
 
 const char* kWebRTCCertsDir = "webrtc_certs_dir";
 
@@ -166,6 +169,9 @@
 const char* kBootImageKernelCmdline = "boot_image_kernel_cmdline";
 const char* kExtraKernelCmdline = "extra_kernel_cmdline";
 
+const char* kFramesServerPort = "frames_server_port";
+const char* kTouchServerPort = "touch_server_port";
+const char* kKeyboardServerPort = "keyboard_server_port";
 }  // namespace
 
 namespace vsoc {
@@ -603,6 +609,54 @@
   (*Dictionary())[kVncServerPort] = vnc_server_port;
 }
 
+int CuttlefishConfig::InstanceSpecific::frames_server_port() const {
+  return (*Dictionary())[kFramesServerPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_frames_server_port(int frames_server_port) {
+  (*Dictionary())[kFramesServerPort] = frames_server_port;
+}
+
+int CuttlefishConfig::InstanceSpecific::touch_server_port() const {
+  return (*Dictionary())[kTouchServerPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_touch_server_port(int touch_server_port) {
+  (*Dictionary())[kTouchServerPort] = touch_server_port;
+}
+
+int CuttlefishConfig::InstanceSpecific::keyboard_server_port() const {
+  return (*Dictionary())[kKeyboardServerPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_keyboard_server_port(int keyboard_server_port) {
+  (*Dictionary())[kKeyboardServerPort] = keyboard_server_port;
+}
+
+int CuttlefishConfig::InstanceSpecific::tombstone_receiver_port() const {
+  return (*Dictionary())[kTombstoneReceiverPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_tombstone_receiver_port(int tombstone_receiver_port) {
+  (*Dictionary())[kTombstoneReceiverPort] = tombstone_receiver_port;
+}
+
+int CuttlefishConfig::InstanceSpecific::logcat_port() const {
+  return (*Dictionary())[kLogcatPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_logcat_port(int logcat_port) {
+  (*Dictionary())[kLogcatPort] = logcat_port;
+}
+
+int CuttlefishConfig::InstanceSpecific::config_server_port() const {
+  return (*Dictionary())[kConfigServerPort].asInt();
+}
+
+void CuttlefishConfig::MutableInstanceSpecific::set_config_server_port(int config_server_port) {
+  (*Dictionary())[kConfigServerPort] = config_server_port;
+}
+
 void CuttlefishConfig::set_enable_sandbox(const bool enable_sandbox) {
   (*dictionary_)[kEnableSandbox] = enable_sandbox;
 }
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index dad3c56..142c257 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -24,6 +24,11 @@
 class Value;
 }
 
+namespace cvd {
+constexpr char kLogcatSerialMode[] = "serial";
+constexpr char kLogcatVsockMode[] = "vsock";
+}
+
 namespace vsoc {
 
 constexpr char kDefaultUuidPrefix[] = "699acfc4-c8c4-11e7-882b-5065f31dc1";
@@ -307,8 +312,29 @@
     const Json::Value* Dictionary() const;
   public:
     std::string serial_number() const;
+    // If any of the following port numbers is 0, the relevant service is not
+    // running on the guest.
+
+    // Port number to connect to vnc server on the host
     int vnc_server_port() const;
+    // Port number to connect to the tombstone receiver on the host
+    int tombstone_receiver_port() const;
+    // Port number to connect to the logcat receiver on the host
+    int logcat_port() const;
+    // Port number to connect to the config server on the host
+    int config_server_port() const;
+    // Port number to connect to the keyboard server on the host. (Only
+    // operational if QEMU is the vmm.)
+    int keyboard_server_port() const;
+    // Port number to connect to the touch server on the host. (Only
+    // operational if QEMU is the vmm.)
+    int touch_server_port() const;
+    // Port number to connect to the frame server on the host. (Only
+    // operational if using swiftshader as the GPU.)
+    int frames_server_port() const;
+    // Port number to connect to the adb server on the host
     int host_port() const;
+    // Port number to connect to the tpm server on the host
     int tpm_port() const;
     std::string adb_ip_and_port() const;
     std::string adb_device_name() const;
@@ -361,6 +387,12 @@
   public:
     void set_serial_number(const std::string& serial_number);
     void set_vnc_server_port(int vnc_server_port);
+    void set_tombstone_receiver_port(int tombstone_receiver_port);
+    void set_logcat_port(int logcat_port);
+    void set_config_server_port(int config_server_port);
+    void set_frames_server_port(int config_server_port);
+    void set_touch_server_port(int config_server_port);
+    void set_keyboard_server_port(int config_server_port);
     void set_host_port(int host_port);
     void set_tpm_port(int tpm_port);
     void set_adb_ip_and_port(const std::string& ip_port);
diff --git a/host/libs/config/kernel_args.cpp b/host/libs/config/kernel_args.cpp
new file mode 100644
index 0000000..531fc99
--- /dev/null
+++ b/host/libs/config/kernel_args.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "host/libs/config/kernel_args.h"
+
+#include <string>
+#include <vector>
+
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/vm_manager.h"
+
+template<typename T>
+static void AppendVector(std::vector<T>* destination, const std::vector<T>& source) {
+  destination->insert(destination->end(), source.begin(), source.end());
+}
+
+template<typename S, typename T>
+static std::string concat(const S& s, const T& t) {
+  std::ostringstream os;
+  os << s << t;
+  return os.str();
+}
+
+std::vector<std::string> KernelCommandLineFromConfig(const vsoc::CuttlefishConfig& config) {
+  auto instance = config.ForDefaultInstance();
+  std::vector<std::string> kernel_cmdline;
+
+  AppendVector(&kernel_cmdline, config.boot_image_kernel_cmdline());
+  AppendVector(&kernel_cmdline,
+               vm_manager::VmManager::ConfigureGpuMode(config.vm_manager(), config.gpu_mode()));
+  AppendVector(&kernel_cmdline, vm_manager::VmManager::ConfigureBootDevices(config.vm_manager()));
+
+  kernel_cmdline.push_back(concat("androidboot.serialno=", instance.serial_number()));
+  kernel_cmdline.push_back(concat("androidboot.lcd_density=", config.dpi()));
+  kernel_cmdline.push_back(concat(
+      "androidboot.setupwizard_mode=", config.setupwizard_mode()));
+  if (!config.use_bootloader()) {
+    std::string slot_suffix;
+    if (config.boot_slot().empty()) {
+      slot_suffix = "_a";
+    } else {
+      slot_suffix = "_" + config.boot_slot();
+    }
+    kernel_cmdline.push_back(concat("androidboot.slot_suffix=", slot_suffix));
+  }
+  kernel_cmdline.push_back(concat("loop.max_part=", config.loop_max_part()));
+  if (config.guest_enforce_security()) {
+    kernel_cmdline.push_back("enforcing=1");
+  } else {
+    kernel_cmdline.push_back("enforcing=0");
+    kernel_cmdline.push_back("androidboot.selinux=permissive");
+  }
+  if (config.guest_audit_security()) {
+    kernel_cmdline.push_back("audit=1");
+  } else {
+    kernel_cmdline.push_back("audit=0");
+  }
+  if (config.guest_force_normal_boot()) {
+    kernel_cmdline.push_back("androidboot.force_normal_boot=1");
+  }
+
+  if (config.enable_tombstone_receiver() && instance.tombstone_receiver_port()) {
+    kernel_cmdline.push_back("androidboot.tombstone_transmit=1");
+    kernel_cmdline.push_back(concat("androidboot.vsock_tombstone_port=", instance.tombstone_receiver_port()));
+  } else {
+    kernel_cmdline.push_back("androidboot.tombstone_transmit=0");
+  }
+
+  if (config.logcat_mode() == cvd::kLogcatVsockMode && instance.logcat_port()) {
+    kernel_cmdline.push_back(concat("androidboot.vsock_logcat_port=", instance.logcat_port()));
+  }
+
+  if (instance.config_server_port()) {
+    kernel_cmdline.push_back(concat("androidboot.cuttlefish_config_server_port=", instance.config_server_port()));
+  }
+
+  if (instance.tpm_port()) {
+    kernel_cmdline.push_back(concat("androidboot.tpm_vsock_port=", instance.tpm_port()));
+  }
+
+  if (instance.keyboard_server_port()) {
+    kernel_cmdline.push_back(concat("androidboot.vsock_keyboard_port=", instance.keyboard_server_port()));
+  }
+
+  if (instance.touch_server_port()) {
+    kernel_cmdline.push_back(concat("androidboot.vsock_touch_port=", instance.touch_server_port()));
+  }
+
+  if (instance.frames_server_port()) {
+    kernel_cmdline.push_back(concat("androidboot.vsock_frames_port=", instance.frames_server_port()));
+  }
+
+  AppendVector(&kernel_cmdline, config.extra_kernel_cmdline());
+
+  return kernel_cmdline;
+}
diff --git a/host/commands/run_cvd/kernel_args.h b/host/libs/config/kernel_args.h
similarity index 61%
rename from host/commands/run_cvd/kernel_args.h
rename to host/libs/config/kernel_args.h
index caf024e..9a0feb7 100644
--- a/host/commands/run_cvd/kernel_args.h
+++ b/host/libs/config/kernel_args.h
@@ -19,12 +19,6 @@
 #include <string>
 #include <vector>
 
-#include "host/commands/run_cvd/launch.h"
 #include "host/libs/config/cuttlefish_config.h"
 
 std::vector<std::string> KernelCommandLineFromConfig(const vsoc::CuttlefishConfig& config);
-std::vector<std::string> KernelCommandLineFromStreamer(const StreamerLaunchResult& vnc_config);
-std::vector<std::string> KernelCommandLineFromTombstone(const TombstoneReceiverPorts& tombstone);
-std::vector<std::string> KernelCommandLineFromConfigServer(const ConfigServerPorts& config_server);
-std::vector<std::string> KernelCommandLineFromLogcatServer(const LogcatServerPorts& config_server);
-std::vector<std::string> KernelCommandLineFromTpm(const TpmPorts& tpm);
diff --git a/host/libs/vm_manager/Android.bp b/host/libs/vm_manager/Android.bp
index 8f4150a..71ca11c 100644
--- a/host/libs/vm_manager/Android.bp
+++ b/host/libs/vm_manager/Android.bp
@@ -33,8 +33,3 @@
     ],
     defaults: ["cuttlefish_host_only"],
 }
-
-sh_binary_host {
-    name: "cf_qemu.sh",
-    src: "cf_qemu.sh",
-}
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
deleted file mode 100755
index ca39612..0000000
--- a/host/libs/vm_manager/cf_qemu.sh
+++ /dev/null
@@ -1,168 +0,0 @@
-#!/bin/bash
-
-#
-# Copyright (C) 2018 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.
-#
-
-print_command() {
-  binary=$1; shift
-  binary_args=("$@")
-  printf %s "${binary}"
-  for i in "${binary_args[@]}"; do
-    case "$i" in
-      -*) printf "\\%s  %s " $'\n' "$i" ;;
-      *) printf "%s " "$i" ;;
-    esac
-  done
-  echo
-}
-
-exec_run() {
-  binary=$1; shift
-  binary_args=("$@")
-  print_command "${binary}" "${binary_args[@]}"
-  exec "${binary}" "${binary_args[@]}"
-}
-
-run() {
-  binary=$1; shift
-  binary_args=("$@")
-  print_command "${binary}" "${binary_args[@]}"
-  "${binary}" "${binary_args[@]}"
-}
-
-default_instance_number() {
-    if [[ "${USER::5}" == "vsoc-" ]]; then
-        echo "${USER: -2}"
-    else
-        echo "01"
-    fi
-}
-CUTTLEFISH_INSTANCE="${CUTTLEFISH_INSTANCE:-$(default_instance_number)}"
-default_instance_name="cvd-${CUTTLEFISH_INSTANCE}"
-default_uuid="699acfc4-c8c4-11e7-882b-5065f31dc1${CUTTLEFISH_INSTANCE}"
-default_dir="${HOME}/cuttlefish_runtime"
-default_internal_dir="${default_internal_dir}"
-default_mobile_tap_name="cvd-mtap-${CUTTLEFISH_INSTANCE}"
-default_wifi_tap_name="cvd-wtap-${CUTTLEFISH_INSTANCE}"
-
-qemu_binary=${qemu_binary=/usr/bin/qemu-system-x86_64}
-dtc_binary=${dtc_binary:-dtc}
-
-if [[ "${qemu_binary##*/}" = "qemu-system-aarch64" ]]; then
-  # On ARM, the early console can be PCI, and ISA is not supported
-  kernel_console_serial="pci-serial"
-  machine="virt,gic_version=2"
-  romfile=",romfile="
-  cpu=cortex-a53
-else
-  # On x86, the early console must be ISA, not PCI, so we start to get kernel
-  # messages as soon as possible. ISA devices do not have 'addr' assignments.
-  kernel_console_serial="isa-serial"
-  machine="pc-i440fx-2.8,accel=kvm"
-  romfile=
-  cpu=host
-fi
-
-# Put anything here that might affect the machine configuration generated by
-# QEMU. Anything which connects statefully to another service (like a socket)
-# should be added in another section below.
-args=(
-    -name "guest=${instance_name:-${default_instance_name}},debug-threads=on"
-    -machine "${machine},usb=off,dump-guest-core=off"
-    -m "${memory_mb:-2048}"
-    -realtime mlock=off
-    -smp "${cpus:-2},sockets=${cpus:-2},cores=1,threads=1"
-    -uuid "${uuid:-${default_uuid}}"
-    -display none
-    -no-user-config
-    -nodefaults
-    -rtc "base=utc"
-    -no-shutdown
-    -boot "strict=on"
-    -kernel "${kernel_image_path:-${HOME}/kernel}"
-    -append "${kernel_cmdline:-"loop.max_part=7 console=ttyS0 androidboot.console=ttyS1 androidboot.hardware=vsoc enforcing=0 audit=1 androidboot.selinux=permissive mac80211_hwsim.radios=0 security=selinux buildvariant=userdebug  androidboot.serialno=CUTTLEFISHCVD01 androidboot.lcd_density=160 androidboot.boot_devices=pci0000:00/0000:00:03.0"}"
-    -device "virtio-serial-pci,id=virtio-serial0"
-)
-
-IFS=';' read -ra virtual_disk_array <<< "$virtual_disk_paths"
-virtual_disk_index=0
-for virtual_disk in "${virtual_disk_array[@]}"; do
-  if [[ $virtual_disk_index == 0 ]]; then
-    bootindex=",bootindex=1"
-  else
-    bootindex=""
-  fi
-  args+=(
-    -drive "file=${virtual_disk},format=qcow2,if=none,id=drive-virtio-disk${virtual_disk_index},aio=threads"
-    -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk${virtual_disk_index},id=virtio-disk${virtual_disk_index}${bootindex}"
-  )
-  virtual_disk_index=$((virtual_disk_index + 1))
-done
-
-args+=(
-    -netdev "tap,id=hostnet0,ifname=${wifi_tap_name:-${default_wifi_tap_name}},script=no,downscript=no"
-    -device "virtio-net-pci,netdev=hostnet0,id=net0${romfile}"
-    -netdev "tap,id=hostnet1,ifname=${mobile_tap_name:-${default_mobile_tap_name}},script=no,downscript=no"
-    -device "virtio-net-pci,netdev=hostnet1,id=net1${romfile}"
-    -device "virtio-balloon-pci,id=balloon0"
-    -object "rng-random,id=objrng0,filename=/dev/urandom"
-    -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000"
-    -cpu "${cpu}"
-    -msg "timestamp=on"
-    -device "AC97"
-)
-
-# The services providing these sockets don't expect multiple connections,
-# so we must not have them in 'args' when we dump the machine FDT. It's
-# OK to add them now, after the dumping and patching has completed.
-# The (maybe patched) DTB can also be provided now.
-
-if [[ "${use_bootloader}" = "true" ]]; then
-  args+=(
-    -bios "${bootloader}"
-  )
-fi
-
-args+=(
-    -chardev "socket,id=charmonitor,path=${monitor_path:-${default_internal_dir}/qemu_monitor.sock},server,nowait"
-    -mon "chardev=charmonitor,id=monitor,mode=control"
-    -chardev "file,id=charserial0,path=${kernel_log_pipe_name:-${default_internal_dir}/kernel-log},append=on"
-    -device "${kernel_console_serial},chardev=charserial0,id=serial0"
-    -chardev "socket,id=charserial1,path=${console_path:-${default_dir}/console},server,nowait"
-    -device "${kernel_console_serial},chardev=charserial1,id=serial1"
-)
-
-if [[ "${logcat_mode}" == "serial" ]]; then
-    args+=(
-        -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
-        -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
-    )
-fi
-
-if [[ -n "${gdb_flag}" ]]; then
-  args+=(-gdb "${gdb_flag}")
-fi
-
-if [[ -n "${ramdisk_image_path}" ]]; then
-  args+=(-initrd "${ramdisk_image_path}")
-fi
-
-if [[ ${vsock_guest_cid:-0} -gt 2 ]]; then
-  args+=(-device "vhost-vsock-pci,guest-cid=${vsock_guest_cid}")
-fi
-
-export QEMU_AUDIO_DRV=none
-exec_run "${qemu_binary}" "${args[@]}"
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index f343765..fe49981 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -187,9 +187,7 @@
   auto wifi_tap = AddTapFdParameter(&crosvm_cmd, instance.wifi_tap_name());
   AddTapFdParameter(&crosvm_cmd, instance.mobile_tap_name());
 
-  if (cvd::HostArch() == "x86_64") {
-    crosvm_cmd.AddParameter("--rw-pmem-device=", instance.access_kregistry_path());
-  }
+  crosvm_cmd.AddParameter("--rw-pmem-device=", instance.access_kregistry_path());
 
   if (config_->enable_sandbox()) {
     const bool seccomp_exists = cvd::DirectoryExists(config_->seccomp_policy_dir());
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 1bfaee0..45df5e3 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -53,21 +53,6 @@
   LOG(INFO) << key << "=" << value;
 }
 
-std::string JoinString(const std::vector<std::string>& args,
-                       const std::string& delim) {
-  bool first = true;
-  std::stringstream output;
-  for (const auto& arg : args) {
-    if (first) {
-      first = false;
-    } else {
-      output << delim;
-    }
-    output << arg;
-  }
-  return output.str();
-}
-
 bool Stop() {
   auto config = vsoc::CuttlefishConfig::Get();
   auto monitor_path = GetMonitorPath(config);
@@ -129,39 +114,160 @@
 
 std::vector<cvd::Command> QemuManager::StartCommands() {
   auto instance = config_->ForDefaultInstance();
-  // Set the config values in the environment
-  LogAndSetEnv("qemu_binary", config_->qemu_binary());
-  LogAndSetEnv("instance_name", instance.instance_name());
-  LogAndSetEnv("memory_mb", std::to_string(config_->memory_mb()));
-  LogAndSetEnv("cpus", std::to_string(config_->cpus()));
-  LogAndSetEnv("uuid", instance.uuid());
-  LogAndSetEnv("monitor_path", GetMonitorPath(config_));
-  LogAndSetEnv("kernel_image_path", config_->GetKernelImageToUse());
-  LogAndSetEnv("gdb_flag", config_->gdb_flag());
-  LogAndSetEnv("ramdisk_image_path", config_->final_ramdisk_path());
-  LogAndSetEnv("kernel_cmdline", kernel_cmdline_);
-  LogAndSetEnv("virtual_disk_paths", JoinString(instance.virtual_disk_paths(),
-                                                ";"));
-  LogAndSetEnv("wifi_tap_name", instance.wifi_tap_name());
-  LogAndSetEnv("mobile_tap_name", instance.mobile_tap_name());
-  LogAndSetEnv("kernel_log_pipe_name", instance.kernel_log_pipe_name());
-  LogAndSetEnv("console_path", instance.console_path());
-  LogAndSetEnv("logcat_path", instance.logcat_path());
-  LogAndSetEnv("vsock_guest_cid", std::to_string(instance.vsock_guest_cid()));
-  LogAndSetEnv("logcat_mode", config_->logcat_mode());
-  LogAndSetEnv("use_bootloader", config_->use_bootloader() ? "true" : "false");
-  LogAndSetEnv("bootloader", config_->bootloader());
 
-  cvd::Command qemu_cmd(vsoc::DefaultHostArtifactsPath("bin/cf_qemu.sh"),
-                        [](cvd::Subprocess* proc) {
-                          auto stopped = Stop();
-                          if (stopped) {
-                            return true;
-                          }
-                          LOG(WARNING) << "Failed to stop VMM nicely, "
-                                       << "attempting to KILL";
-                          return KillSubprocess(proc);
-                        });
+  auto stop = [](cvd::Subprocess* proc) {
+    auto stopped = Stop();
+    if (stopped) {
+      return true;
+    }
+    LOG(WARNING) << "Failed to stop VMM nicely, "
+                  << "attempting to KILL";
+    return KillSubprocess(proc);
+  };
+
+  bool is_arm = android::base::EndsWith(config_->qemu_binary(), "system-aarch64");
+
+  cvd::Command qemu_cmd(config_->qemu_binary(), stop);
+  qemu_cmd.AddParameter("-name");
+  qemu_cmd.AddParameter("guest=", instance.instance_name(), ",debug-threads=on");
+
+  qemu_cmd.AddParameter("-machine");
+  auto machine = is_arm ? "virt,gic_version=2" : "pc-i440fx-2.8,accel=kvm";
+  qemu_cmd.AddParameter(machine, ",usb=off,dump-guest-core=off");
+
+  qemu_cmd.AddParameter("-m");
+  qemu_cmd.AddParameter(config_->memory_mb());
+
+  qemu_cmd.AddParameter("-realtime");
+  qemu_cmd.AddParameter("mlock=off");
+
+  qemu_cmd.AddParameter("-smp");
+  qemu_cmd.AddParameter(config_->cpus(), ",sockets=", config_->cpus(),
+                        ",cores=1,threads=1");
+
+  qemu_cmd.AddParameter("-uuid");
+  qemu_cmd.AddParameter(instance.uuid());
+
+  qemu_cmd.AddParameter("-display");
+  qemu_cmd.AddParameter("none");
+
+  qemu_cmd.AddParameter("-no-user-config");
+  qemu_cmd.AddParameter("-nodefaults");
+  qemu_cmd.AddParameter("-no-shutdown");
+
+  qemu_cmd.AddParameter("-rtc");
+  qemu_cmd.AddParameter("base=utc");
+
+  qemu_cmd.AddParameter("-boot");
+  qemu_cmd.AddParameter("strict=on");
+
+  qemu_cmd.AddParameter("-kernel");
+  qemu_cmd.AddParameter(config_->GetKernelImageToUse());
+
+  qemu_cmd.AddParameter("-append");
+  qemu_cmd.AddParameter(kernel_cmdline_);
+
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter("virtio-serial-pci,id=virtio-serial0");
+
+  for (size_t i = 0; i < instance.virtual_disk_paths().size(); i++) {
+    auto bootindex = i == 0 ? ",bootindex=1" : "";
+    auto disk = instance.virtual_disk_paths()[i];
+    qemu_cmd.AddParameter("-drive");
+    qemu_cmd.AddParameter("file=", disk, ",if=none,id=drive-virtio-disk", i,
+                          ",aio=threads");
+    qemu_cmd.AddParameter("-device");
+    qemu_cmd.AddParameter("virtio-blk-pci,scsi=off,drive=drive-virtio-disk", i,
+                          ",id=virtio-disk", i, bootindex);
+  }
+
+  qemu_cmd.AddParameter("-netdev");
+  qemu_cmd.AddParameter("tap,id=hostnet0,ifname=", instance.wifi_tap_name(),
+                        ",script=no,downscript=no");
+
+  auto romfile = is_arm ? ",romfile" : "";
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet0,id=net0", romfile);
+
+  qemu_cmd.AddParameter("-netdev");
+  qemu_cmd.AddParameter("tap,id=hostnet1,ifname=", instance.mobile_tap_name(),
+                        ",script=no,downscript=no");
+
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter("virtio-net-pci,netdev=hostnet1,id=net1", romfile);
+
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter("virtio-balloon-pci,id=balloon0");
+
+  qemu_cmd.AddParameter("-object");
+  qemu_cmd.AddParameter("rng-random,id=objrng0,filename=/dev/urandom");
+
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter("virtio-rng-pci,rng=objrng0,id=rng0,",
+                        "max-bytes=1024,period=2000");
+
+  qemu_cmd.AddParameter("-cpu");
+  qemu_cmd.AddParameter(is_arm ? "cortex-a53" : "host");
+
+  qemu_cmd.AddParameter("-msg");
+  qemu_cmd.AddParameter("timestamp=on");
+
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter("AC97");
+
+  if (config_->use_bootloader()) {
+    qemu_cmd.AddParameter("-bios");
+    qemu_cmd.AddParameter(config_->bootloader());
+  }
+
+  qemu_cmd.AddParameter("-chardev");
+  qemu_cmd.AddParameter("socket,id=charmonitor,path=", GetMonitorPath(config_),
+                        ",server,nowait");
+
+  qemu_cmd.AddParameter("-mon");
+  qemu_cmd.AddParameter("chardev=charmonitor,id=monitor,mode=control");
+
+  qemu_cmd.AddParameter("-chardev");
+  qemu_cmd.AddParameter("file,id=charserial0,path=",
+                        instance.kernel_log_pipe_name(), ",append=on");
+
+  qemu_cmd.AddParameter("-device");
+  // On ARM, the early console can be PCI, and ISA is not supported
+  // On x86, the early console must be ISA, not PCI, so we start to get kernel
+  // messages as soon as possible. ISA devices do not have 'addr' assignments.
+  auto kernel_console_serial = is_arm ? "pci-serial" : "isa-serial";
+  qemu_cmd.AddParameter(kernel_console_serial, ",chardev=charserial0,id=serial0");
+
+  qemu_cmd.AddParameter("-chardev");
+  qemu_cmd.AddParameter("socket,id=charserial1,path=", instance.console_path(),
+                        ",server,nowait");
+
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter(kernel_console_serial, ",chardev=charserial1,id=serial1");
+
+  if (config_->logcat_mode() == "serial") {
+    qemu_cmd.AddParameter("-chardev");
+    qemu_cmd.AddParameter("file,id=charchannel0,path=", instance.logcat_path(),
+                          ",append=on");
+
+    qemu_cmd.AddParameter("-device");
+    qemu_cmd.AddParameter("virtserialport,bus=virtio-serial0.0,nr=1,",
+                          "chardev=charchannel0,id=channel0,name=cf-logcat");
+  }
+
+  if (config_->gdb_flag().size() > 0) {
+    qemu_cmd.AddParameter("-gdb");
+    qemu_cmd.AddParameter(config_->gdb_flag());
+  }
+
+  qemu_cmd.AddParameter("-initrd");
+  qemu_cmd.AddParameter(config_->final_ramdisk_path());
+
+  qemu_cmd.AddParameter("-device");
+  qemu_cmd.AddParameter("vhost-vsock-pci,guest-cid=", instance.vsock_guest_cid());
+
+  LogAndSetEnv("QEMU_AUDIO_DRV", "none");
+
   std::vector<cvd::Command> ret;
   ret.push_back(std::move(qemu_cmd));
   return ret;
diff --git a/host_package.mk b/host_package.mk
index 55278f0..18176c5 100644
--- a/host_package.mk
+++ b/host_package.mk
@@ -28,7 +28,6 @@
     adb_connector \
     stop_cvd \
     vnc_server \
-    cf_qemu.sh \
     kernel_log_monitor \
     extract-vmlinux \
     crosvm \
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index eccf70c..542ada7 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -75,6 +75,9 @@
 BOARD_CACHEIMAGE_PARTITION_SIZE := 67108864
 BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
 
+# Use ext4 block sharing on read-only partitions
+BOARD_EXT4_SHARE_DUP_BLOCKS := true
+
 BOARD_GPU_DRIVERS := virgl
 
 # Enable goldfish's encoder.
diff --git a/shared/config/fstab.ext4 b/shared/config/fstab.ext4
new file mode 100644
index 0000000..3e3672f
--- /dev/null
+++ b/shared/config/fstab.ext4
@@ -0,0 +1,14 @@
+boot /boot emmc defaults recoveryonly
+system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+# Add all non-dynamic partitions except system, after this comment
+/dev/block/by-name/userdata /data ext4 nodev,noatime,nosuid,errors=panic wait,fileencryption=aes-256-xts:aes-256-cts,fsverity
+/dev/block/by-name/cache /cache ext4 nodev,noatime,nosuid,errors=panic wait
+/dev/block/by-name/metadata /metadata ext4 nodev,noatime,nosuid,errors=panic wait,formattable,first_stage_mount
+/dev/block/by-name/misc /misc emmc defaults defaults
+# Add all dynamic partitions except system, after this comment
+odm /odm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+product /product ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+system_ext /system_ext ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+vendor /vendor ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
+/dev/block/zram0 none swap defaults zramsize=75%
+/tmp /sdcard none defaults,bind recoveryonly
diff --git a/shared/device.mk b/shared/device.mk
index 57deeb3..c0023c6 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -75,6 +75,9 @@
 PRODUCT_PROPERTY_OVERRIDES += \
     wlan.driver.status=ok
 
+# Enforce privapp-permissions whitelist.
+PRODUCT_PROPERTY_OVERRIDES += ro.control_privapp_permissions=enforce
+
 # aes-256-heh default is not supported in standard kernels.
 PRODUCT_PROPERTY_OVERRIDES += ro.crypto.volume.filenames_mode=aes-256-cts
 
@@ -164,6 +167,7 @@
     device/google/cuttlefish/shared/config/media_codecs_google_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_video.xml \
     device/google/cuttlefish/shared/config/media_codecs_performance.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_performance.xml \
     device/google/cuttlefish/shared/config/media_profiles.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml \
+    device/google/cuttlefish/shared/permissions/privapp-permissions-cuttlefish.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/privapp-permissions-cuttlefish.xml \
     frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
     frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_audio.xml \
     frameworks/av/media/libstagefright/data/media_codecs_google_telephony.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_telephony.xml \
@@ -197,9 +201,20 @@
     frameworks/native/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.vulkan.deqp.level.xml \
     system/bt/vendor_libs/test_vendor_lib/data/controller_properties.json:vendor/etc/bluetooth/controller_properties.json \
     device/google/cuttlefish/shared/config/task_profiles.json:$(TARGET_COPY_OUT_VENDOR)/etc/task_profiles.json \
+
+ifeq ($(TARGET_USERDATAIMAGE_FILE_SYSTEM_TYPE),f2fs)
+PRODUCT_COPY_FILES += \
     device/google/cuttlefish/shared/config/fstab:$(TARGET_COPY_OUT_RAMDISK)/fstab.cutf_cvm \
     device/google/cuttlefish/shared/config/fstab:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.cutf_cvm \
-    device/google/cuttlefish/shared/config/fstab:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.cutf_cvm \
+    device/google/cuttlefish/shared/config/fstab:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.cutf_cvm
+endif
+
+ifeq ($(TARGET_USERDATAIMAGE_FILE_SYSTEM_TYPE),ext4)
+PRODUCT_COPY_FILES += \
+    device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_RAMDISK)/fstab.cutf_cvm \
+    device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_VENDOR)/etc/fstab.cutf_cvm \
+    device/google/cuttlefish/shared/config/fstab.ext4:$(TARGET_COPY_OUT_RECOVERY)/root/first_stage_ramdisk/fstab.cutf_cvm
+endif
 
 # Packages for HAL implementations
 
diff --git a/shared/permissions/privapp-permissions-cuttlefish.xml b/shared/permissions/privapp-permissions-cuttlefish.xml
new file mode 100644
index 0000000..7c3dcaa
--- /dev/null
+++ b/shared/permissions/privapp-permissions-cuttlefish.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<permissions>
+    <privapp-permissions package="com.android.google.gce.gceservice">
+        <permission name="android.permission.ACCESS_NETWORK_STATE" />
+        <permission name="android.permission.ACCESS_WIFI_STATE" />
+        <permission name="android.permission.CHANGE_WIFI_STATE" />
+        <permission name="android.permission.FOREGROUND_SERVICE" />
+        <permission name="android.permission.INTERNET" />
+        <permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
+        <permission name="android.permission.WRITE_EXTERNAL_STORAGE" />
+        <permission name="android.permission.WRITE_SETTINGS" />
+        <permission name="android.permission.BLUETOOTH" />
+    </privapp-permissions>
+</permissions>
diff --git a/shared/sepolicy/vendor/seapp_contexts b/shared/sepolicy/vendor/seapp_contexts
index e325c99..820c416 100644
--- a/shared/sepolicy/vendor/seapp_contexts
+++ b/shared/sepolicy/vendor/seapp_contexts
@@ -1,2 +1,2 @@
 # GceService app
-user=system seinfo=platform name=com.android.google.gce.gceservice domain=gceservice type=app_data_file
+user=_app isPrivApp=true seinfo=default name=com.android.google.gce.gceservice domain=gceservice type=app_data_file