Merge "Fix OOB vulnerability in setGsm/CdmaSmsBroadcastConfigInfo"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index ef710ca..8c7bc4b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,7 +12,7 @@
       "name": "vts_treble_vintf_vendor_test"
     },
     {
-      "name": "hidl_implementation_test"
+      "name": "hal_implementation_test"
     }
   ]
 }
diff --git a/common/libs/threads/Android.bp b/common/libs/threads/Android.bp
deleted file mode 100644
index 97b4be8..0000000
--- a/common/libs/threads/Android.bp
+++ /dev/null
@@ -1,31 +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.
-
-cc_test_host {
-    name: "cuttlefish_thread_test",
-    srcs: [
-        "cuttlefish_thread_test.cpp",
-    ],
-    shared_libs: [
-        "cuttlefish_time",
-        "libbase",
-    ],
-    static_libs: [
-        "libgtest_host",
-    ],
-    cpp_std: "c++17",
-    defaults: ["cuttlefish_host_only"],
-    test_suites: ["general-tests"],
-}
diff --git a/common/libs/threads/TEST_MAPPING b/common/libs/threads/TEST_MAPPING
deleted file mode 100644
index 87f7884..0000000
--- a/common/libs/threads/TEST_MAPPING
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "cuttlefish_thread_test",
-      "host": true
-    }
-  ]
-}
diff --git a/common/libs/threads/cuttlefish_thread.h b/common/libs/threads/cuttlefish_thread.h
deleted file mode 100644
index 54e5ceb..0000000
--- a/common/libs/threads/cuttlefish_thread.h
+++ /dev/null
@@ -1,169 +0,0 @@
-#pragma once
-/*
- * Copyright (C) 2016 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.
- */
-
-// Concurreny classess for cuttlefish.
-//
-// These more or less mimic the interface of the C++ classes:
-//   Mutex is similar to std::mutex
-//   ConditionVariable is similar to std::condition_variable
-//   LockGuard is similar to std::lock_guard
-//
-// There are some extensions:
-//   ScopedThread creates a Thread and joins it when the class is destroyed
-//   This comes in handy during unit tests. It should be used cautiously, if
-//   at all, in production code because thread creation isn't free.
-
-#include <stdint.h>
-#include <pthread.h>
-#include "common/libs/time/monotonic_time.h"
-
-namespace cvd {
-
-class Mutex {
- friend class ConditionVariable;
-
- public:
-  Mutex() {
-    pthread_mutex_init(&mutex_, NULL);
-  }
-
-  ~Mutex() {
-    pthread_mutex_destroy(&mutex_);
-  }
-
-  void Lock() {
-    pthread_mutex_lock(&mutex_);
-  }
-
-  void Unlock() {
-    pthread_mutex_unlock(&mutex_);
-  }
-
-  // TODO(ghartman): Add TryLock if and only if there's a good use case.
-
- protected:
-
-  pthread_mutex_t* GetMutex() {
-    return &mutex_;
-  }
-
-  pthread_mutex_t mutex_;
-
- private:
-  Mutex(const Mutex&);
-  Mutex& operator= (const Mutex&);
-};
-
-class ConditionVariable {
- public:
-  explicit ConditionVariable(Mutex* mutex) : mutex_(mutex) {
-    pthread_condattr_t attr;
-    pthread_condattr_init(&attr);
-    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
-    pthread_cond_init(&cond_, &attr);
-    pthread_condattr_destroy(&attr);
-  }
-
-  ~ConditionVariable() {
-    pthread_cond_destroy(&cond_);
-  }
-
-  int NotifyOne() {
-    return pthread_cond_signal(&cond_);
-  }
-
-  int NotifyAll() {
-    return pthread_cond_broadcast(&cond_);
-  }
-
-  int Wait() {
-    return pthread_cond_wait(&cond_, mutex_->GetMutex());
-  }
-
-  int WaitUntil(const cvd::time::MonotonicTimePoint& tp) {
-    struct timespec ts;
-    tp.ToTimespec(&ts);
-    return pthread_cond_timedwait(&cond_, mutex_->GetMutex(), &ts);
-  }
-
- protected:
-  Mutex* mutex_;
-  pthread_cond_t cond_;
-
- private:
-  ConditionVariable(const ConditionVariable&);
-  ConditionVariable& operator= (const ConditionVariable&);
-};
-
-template <typename M> class LockGuard {
- public:
-  explicit LockGuard(M& mutex) : mutex_(mutex) {
-    mutex_.Lock();
-  }
-
-  ~LockGuard() {
-    mutex_.Unlock();
-  }
-
- private:
-  M& mutex_;
-
-  LockGuard(const LockGuard&);
-  LockGuard& operator= (const LockGuard&);
-};
-
-// Use only in cases where the mutex can't be upgraded to a Mutex.
-template<> class LockGuard<pthread_mutex_t> {
- public:
-  explicit LockGuard(pthread_mutex_t& mutex) : mutex_(mutex), unlock_(false) {
-    unlock_ = (pthread_mutex_lock(&mutex_) == 0);
-  }
-
-  ~LockGuard() {
-    if (unlock_) {
-      pthread_mutex_unlock(&mutex_);
-    }
-  }
-
- private:
-  pthread_mutex_t& mutex_;
-  bool unlock_;
-
-  LockGuard(const LockGuard&);
-  LockGuard& operator= (const LockGuard&);
-};
-
-class ScopedThread {
- public:
-  ScopedThread(void* (*start)(void*), void* arg) {
-    pthread_create(&thread_, NULL, start, arg);
-  }
-
-  ~ScopedThread() {
-    void* value;
-    pthread_join(thread_, &value);
-  }
-
- protected:
-  pthread_t thread_;
-
- private:
-  ScopedThread(const ScopedThread&);
-  ScopedThread& operator= (const ScopedThread&);
-};
-
-}  // namespace cvd
diff --git a/common/libs/threads/cuttlefish_thread_test.cpp b/common/libs/threads/cuttlefish_thread_test.cpp
deleted file mode 100644
index 5989e9b..0000000
--- a/common/libs/threads/cuttlefish_thread_test.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2016 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 "common/libs/threads/cuttlefish_thread.h"
-
-#include <gtest/gtest.h>
-
-#include <android-base/logging.h>
-#include "common/libs/threads/thunkers.h"
-#include "common/libs/time/monotonic_time.h"
-
-using cvd::ConditionVariable;
-using cvd::Mutex;
-using cvd::ScopedThread;
-using cvd::time::MonotonicTimePoint;
-using cvd::time::Milliseconds;
-
-static const int FINISHED = 100;
-
-static void SleepUntil(const MonotonicTimePoint& in) {
-  struct timespec ts;
-  in.ToTimespec(&ts);
-#ifdef CLOCK_MONOTONIC_RAW
-  // WARNING:
-  // While we do have CLOCK_MONOTONIC_RAW, we can't depend on it until:
-  // - ALL places relying on MonotonicTimePoint are fixed,
-  // - pthread supports pthread_timewait_monotonic.
-  // - CLOCK_MONOTONIC_RAW is re-enabled in monotonic_time.h.
-  //
-  // This is currently observable as a LEGITIMATE problem while running
-  // this test. DO NOT revert this to CLOCK_MONOTONIC_RAW until this is
-  // fixed everywhere AND this test passes.
-  clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
-#else
-  clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
-#endif
-}
-
-class MutexTest {
- public:
-  MutexTest() : busy_(NULL), stage_(0) {}
-
-  void Run() {
-    {
-      ScopedThread thread_a(cvd::thunk<void, &MutexTest::FastThread>, this);
-      ScopedThread thread_b(cvd::thunk<void, &MutexTest::SlowThread>, this);
-    }
-    LOG(INFO) << "MutexTest: completed at stage "
-              << stage_
-              << ", result: "
-              << ((stage_ == FINISHED) ? "PASSED" : "FAILED");
-  }
-
-protected:
-  void* FastThread() {
-    mutex_.Lock();
-    CHECK(busy_ == NULL);
-    busy_ = "FastThread";
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
-    stage_ = 1;
-    busy_ = NULL;
-    mutex_.Unlock();
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(10));
-    mutex_.Lock();
-    CHECK(busy_ == NULL);
-    busy_ = "FastThread";
-    CHECK(stage_ == 2);
-    stage_ = FINISHED;
-    busy_ = NULL;
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  void* SlowThread() {
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(50));
-    mutex_.Lock();
-    CHECK(busy_== NULL);
-    busy_ = "SlowThread";
-    CHECK(stage_ == 1);
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
-    stage_ = 2;
-    busy_ = NULL;
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  Mutex mutex_;
-  const char* busy_;
-  int stage_;
-};
-
-class NotifyOneTest {
- public:
-  NotifyOneTest() : cond_(&mutex_), signalled_(0) {}
-
-  void Run() {
-    {
-      ScopedThread thread_s(
-          cvd::thunk<void, &NotifyOneTest::SignalThread>, this);
-      ScopedThread thread_w1(
-          cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
-      ScopedThread thread_w2(
-          cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
-    }
-    LOG(INFO) << "NotifyOneTest: completed, signalled "
-              << signalled_
-              << ", result: "
-              << ((signalled_ == 2) ? "PASSED" : "FAILED");
-  }
-
-protected:
-  void* SignalThread() {
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
-    mutex_.Lock();
-    cond_.NotifyOne();
-    mutex_.Unlock();
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
-    mutex_.Lock();
-    CHECK(signalled_== 1);
-    cond_.NotifyOne();
-    mutex_.Unlock();
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
-    mutex_.Lock();
-    CHECK(signalled_ == 2);
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  void* WaitThread() {
-    mutex_.Lock();
-    cond_.Wait();
-    signalled_++;
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  Mutex mutex_;
-  ConditionVariable cond_;
-  int signalled_;
-};
-
-class NotifyAllTest {
- public:
-  NotifyAllTest() : cond_(&mutex_), signalled_(0) {}
-
-  void Run() {
-    {
-      ScopedThread thread_s(
-          cvd::thunk<void, &NotifyAllTest::SignalThread>, this);
-      ScopedThread thread_w1(
-          cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
-      ScopedThread thread_w2(
-          cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
-    }
-    printf("NotifyAllTest: completed, signalled %d (%s)\n",
-           signalled_, (signalled_ == 2) ? "PASSED" : "FAILED");
-  }
-
-protected:
-  void* SignalThread() {
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
-    mutex_.Lock();
-    cond_.NotifyAll();
-    mutex_.Unlock();
-    SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
-    mutex_.Lock();
-    CHECK(signalled_ == 2);
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  void* WaitThread() {
-    mutex_.Lock();
-    cond_.Wait();
-    signalled_++;
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  Mutex mutex_;
-  ConditionVariable cond_;
-  int signalled_;
-};
-
-class WaitUntilTest {
- public:
-  WaitUntilTest() : cond_(&mutex_), stage_(0) {}
-
-  bool Run() {
-    start_ = MonotonicTimePoint::Now();
-    {
-      ScopedThread thread_s(
-          cvd::thunk<void, &WaitUntilTest::SignalThread>, this);
-      ScopedThread thread_w2(
-          cvd::thunk<void, &WaitUntilTest::WaitThread>, this);
-    }
-    printf("WaitUntilTest: completed, stage %d (%s)\n",
-           stage_, (stage_ == FINISHED) ? "PASSED" : "FAILED");
-    return stage_ == FINISHED;
-  }
-
-protected:
-  void* SignalThread() {
-    SleepUntil(start_ + Milliseconds(200));
-    mutex_.Lock();
-    CHECK(stage_ == 2);
-    cond_.NotifyOne();
-    stage_ = 3;
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  void* WaitThread() {
-    mutex_.Lock();
-    CHECK(stage_ == 0);
-    stage_ = 1;
-    cond_.WaitUntil(start_ + Milliseconds(50));
-    MonotonicTimePoint current(MonotonicTimePoint::Now());
-    CHECK(Milliseconds(current - start_).count() >= 50);
-    CHECK(Milliseconds(current - start_).count() <= 100);
-    stage_ = 2;
-    cond_.WaitUntil(start_ + Milliseconds(1000));
-    current = MonotonicTimePoint::Now();
-    CHECK(Milliseconds(current - start_).count() <= 500);
-    CHECK(stage_ == 3);
-    stage_ = FINISHED;
-    mutex_.Unlock();
-    return NULL;
-  }
-
-  Mutex mutex_;
-  ConditionVariable cond_;
-  int stage_;
-  MonotonicTimePoint start_;
-};
-
-TEST(ThreadTest, Mutex) {
-  MutexTest mt;
-  mt.Run();
-  NotifyOneTest nt1;
-  nt1.Run();
-  NotifyAllTest nta;
-  nta.Run();
-  WaitUntilTest wu;
-  bool success = wu.Run();
-  EXPECT_TRUE(success);
-}
diff --git a/common/libs/threads/thunkers.h b/common/libs/threads/thunkers.h
deleted file mode 100644
index bb97174..0000000
--- a/common/libs/threads/thunkers.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
-#define CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
-
-namespace cvd {
-namespace internal {
-
-template <typename HalType, typename F>
-struct ThunkerImpl;
-
-template <typename HalType, typename Impl, typename R, typename... Args>
-struct ThunkerImpl<HalType, R (Impl::*)(Args...)> {
-  template <R (Impl::*MemFn)(Args...)>
-  static R call(HalType* in, Args... args) {
-    return (reinterpret_cast<Impl*>(in)->*MemFn)(args...);
-  }
-};
-
-template <typename HalType, typename Impl, typename R, typename... Args>
-struct ThunkerImpl<HalType, R (Impl::*)(Args...) const> {
-  template <R (Impl::*MemFn)(Args...) const>
-  static R call(const HalType* in, Args... args) {
-    return (reinterpret_cast<const Impl*>(in)->*MemFn)(args...);
-  }
-};
-
-template <typename HalType, auto MemFunc>
-struct Thunker {
-  static constexpr auto call =
-      ThunkerImpl<HalType, decltype(MemFunc)>::template call<MemFunc>;
-};
-
-}  // namespace internal
-
-template <typename HalType, auto MemFunc>
-constexpr auto thunk = internal::Thunker<HalType, MemFunc>::call;
-
-}  // namespace cvd
-
-#endif
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index fcfa903..807f18c 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -53,6 +53,7 @@
         "libz",
     ],
     static_libs: [
+        "libsparse",
         "libcuttlefish_host_config",
         "libcuttlefish_vm_manager",
         "libgflags",
diff --git a/host/commands/assemble_cvd/data_image.cc b/host/commands/assemble_cvd/data_image.cc
index c84cb33..1527d3c 100644
--- a/host/commands/assemble_cvd/data_image.cc
+++ b/host/commands/assemble_cvd/data_image.cc
@@ -15,9 +15,9 @@
 const int FSCK_ERROR_CORRECTED_REQUIRES_REBOOT = 2;
 
 bool ForceFsckImage(const char* data_image) {
-  int fsck_status = cvd::execute({"/sbin/e2fsck", "-y", "-f", data_image});
+  int fsck_status = cvd::execute({"/sbin/fsck.f2fs", "-y", "-f", data_image});
   if (fsck_status & ~(FSCK_ERROR_CORRECTED|FSCK_ERROR_CORRECTED_REQUIRES_REBOOT)) {
-    LOG(ERROR) << "`e2fsck -y -f " << data_image << "` failed with code "
+    LOG(ERROR) << "`fsck.f2fs -y -f " << data_image << "` failed with code "
                << fsck_status;
     return false;
   }
@@ -46,9 +46,9 @@
     if (!fsck_success) {
       return false;
     }
-    int resize_status = cvd::execute({"/sbin/resize2fs", data_image});
+    int resize_status = cvd::execute({"/sbin/resize.f2fs", data_image});
     if (resize_status != 0) {
-      LOG(ERROR) << "`resize2fs " << data_image << "` failed with code "
+      LOG(ERROR) << "`resize.f2fs " << data_image << "` failed with code "
                  << resize_status;
       return false;
     }
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 0ec8ea2..241912b 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -1,6 +1,10 @@
 #include "host/commands/assemble_cvd/flags.h"
 
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/statvfs.h>
+#include <unistd.h>
 
 #include <algorithm>
 #include <iostream>
@@ -37,7 +41,7 @@
             "'always_create'.");
 DEFINE_int32(blank_data_image_mb, 0,
              "The size of the blank data image to generate, MB.");
-DEFINE_string(blank_data_image_fmt, "ext4",
+DEFINE_string(blank_data_image_fmt, "f2fs",
               "The fs format for the blank data image. Used with mkfs.");
 DEFINE_string(qemu_gdb, "",
               "Debug flag to pass to qemu. e.g. -qemu_gdb=tcp::1234");
@@ -68,10 +72,6 @@
               "be vendor_boot.img in the directory specified by -system_image_dir.");
 DEFINE_int32(memory_mb, 2048,
              "Total amount of memory available for guest, MB.");
-DEFINE_string(mobile_interface, ForCurrentInstance("cvd-mbr-"),
-              "Network interface to use for mobile networking");
-DEFINE_string(mobile_tap_name, ForCurrentInstance("cvd-mtap-"),
-              "The name of the tap interface to use for mobile");
 DEFINE_string(serial_number, ForCurrentInstance("CUTTLEFISHCVD"),
               "Serial number to use for the device");
 DEFINE_string(assembly_dir,
@@ -93,13 +93,15 @@
 DEFINE_string(misc_image, "",
               "Location of the misc partition image. If the image does not "
               "exist, a blank new misc partition image is created.");
-DEFINE_string(composite_disk, "", "Location of the composite disk image. "
-                                  "If empty, a composite disk is not used.");
+DEFINE_string(composite_disk, "", "Location of the composite disk image. ");
 
 DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
             " host kernel. This is only used during transition of our clients."
             " Will be deprecated soon.");
-DEFINE_bool(start_vnc_server, false, "Whether to start the vnc server process.");
+DEFINE_bool(start_vnc_server, false, "Whether to start the vnc server process. "
+                                     "The VNC server runs at port 6443 + i for "
+                                     "the vsoc-i user or CUTTLEFISH_INSTANCE=i, "
+                                     "starting from 1.");
 
 DEFINE_bool(start_webrtc, false, "Whether to start the webrtc process.");
 
@@ -123,8 +125,6 @@
         false,
         "If enabled, exposes local adb service through a websocket.");
 
-DEFINE_int32(vnc_server_port, ForCurrentInstance(6444),
-             "The port on which the vnc server should listen");
 DEFINE_string(adb_mode, "vsock_half_tunnel",
               "Mode for ADB connection."
               "'vsock_tunnel' for a TCP connection tunneled through vsock, "
@@ -135,11 +135,6 @@
 DEFINE_bool(run_adb_connector, true,
             "Maintain adb connection by sending 'adb connect' commands to the "
             "server. Only relevant with -adb_mode=tunnel or vsock_tunnel");
-DEFINE_string(wifi_tap_name, ForCurrentInstance("cvd-wtap-"),
-              "The name of the tap interface to use for wifi");
-DEFINE_int32(vsock_guest_cid,
-             vsoc::GetDefaultPerInstanceVsockCid(),
-             "Guest identifier for vsock. Disabled if under 3.");
 
 DEFINE_string(uuid, vsoc::ForCurrentInstance(vsoc::kDefaultUuidPrefix),
               "UUID to use for the device. Random if not specified");
@@ -169,6 +164,12 @@
              "the slot will be chosen based on the misc partition if using a "
              "bootloader. It will default to 'a' if empty and not using a "
              "bootloader.");
+DEFINE_int32(num_instances, 1, "Number of Android guests to launch");
+DEFINE_bool(resume, true, "Resume using the disk from the last session, if "
+                          "possible. i.e., if --noresume is passed, the disk "
+                          "will be reset to the state it was initially launched "
+                          "in. This flag is ignored if the underlying partition "
+                          "images have been updated since the first launch.");
 
 namespace {
 
@@ -222,19 +223,20 @@
   return config.ForDefaultInstance().PerInstancePath("cuttlefish_config.json");
 }
 
-int GetHostPort() {
-  constexpr int kFirstHostPort = 6520;
-  return vsoc::ForCurrentInstance(kFirstHostPort);
-}
-
 int NumStreamers() {
   auto start_flags = {FLAGS_start_vnc_server, FLAGS_start_webrtc};
   return std::count(start_flags.begin(), start_flags.end(), true);
 }
 
+std::string StrForInstance(const std::string& prefix, int num) {
+  std::ostringstream stream;
+  stream << prefix << std::setfill('0') << std::setw(2) << num;
+  return stream.str();
+}
+
 // Initializes the config object and saves it to file. It doesn't return it, all
 // further uses of the config should happen through the singleton
-bool InitializeCuttlefishConfiguration(
+vsoc::CuttlefishConfig InitializeCuttlefishConfiguration(
     const cvd::BootImageUnpacker& boot_image_unpacker,
     const cvd::FetcherConfig& fetcher_config) {
   // At most one streamer can be started.
@@ -242,28 +244,20 @@
 
   vsoc::CuttlefishConfig tmp_config_obj;
   tmp_config_obj.set_assembly_dir(FLAGS_assembly_dir);
-  auto instance = tmp_config_obj.ForDefaultInstance();
-  // Set this first so that calls to PerInstancePath below are correct
-  instance.set_instance_dir(FLAGS_instance_dir);
   if (!vm_manager::VmManager::IsValidName(FLAGS_vm_manager)) {
-    LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
-    return false;
+    LOG(FATAL) << "Invalid vm_manager: " << FLAGS_vm_manager;
   }
   if (!vm_manager::VmManager::IsValidName(FLAGS_vm_manager)) {
-    LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
-    return false;
+    LOG(FATAL) << "Invalid vm_manager: " << FLAGS_vm_manager;
   }
   tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
   tmp_config_obj.set_gpu_mode(FLAGS_gpu_mode);
   if (vm_manager::VmManager::ConfigureGpuMode(tmp_config_obj.vm_manager(),
                                               tmp_config_obj.gpu_mode()).empty()) {
-    LOG(ERROR) << "Invalid gpu_mode=" << FLAGS_gpu_mode <<
+    LOG(FATAL) << "Invalid gpu_mode=" << FLAGS_gpu_mode <<
                " does not work with vm_manager=" << FLAGS_vm_manager;
-    return false;
   }
 
-  instance.set_serial_number(FLAGS_serial_number);
-
   tmp_config_obj.set_cpus(FLAGS_cpus);
   tmp_config_obj.set_memory_mb(FLAGS_memory_mb);
 
@@ -275,10 +269,6 @@
   tmp_config_obj.set_gdb_flag(FLAGS_qemu_gdb);
   std::vector<std::string> adb = android::base::Split(FLAGS_adb_mode, ",");
   tmp_config_obj.set_adb_mode(std::set<std::string>(adb.begin(), adb.end()));
-  instance.set_host_port(GetHostPort());
-  instance.set_adb_ip_and_port("127.0.0.1:" + std::to_string(GetHostPort()));
-
-  instance.set_device_title(FLAGS_device_title);
   std::string discovered_kernel = fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath);
   std::string foreign_kernel = FLAGS_kernel_path.size() ? FLAGS_kernel_path : discovered_kernel;
   if (foreign_kernel.size()) {
@@ -298,8 +288,7 @@
   auto ramdisk_path = tmp_config_obj.AssemblyPath("ramdisk.img");
   auto vendor_ramdisk_path = tmp_config_obj.AssemblyPath("vendor_ramdisk.img");
   if (!boot_image_unpacker.HasRamdiskImage()) {
-    LOG(INFO) << "A ramdisk is required, but the boot image did not have one.";
-    return false;
+    LOG(FATAL) << "A ramdisk is required, but the boot image did not have one.";
   }
 
   tmp_config_obj.set_boot_image_kernel_cmdline(boot_image_unpacker.kernel_cmdline());
@@ -309,8 +298,6 @@
   tmp_config_obj.set_guest_force_normal_boot(FLAGS_guest_force_normal_boot);
   tmp_config_obj.set_extra_kernel_cmdline(FLAGS_extra_kernel_cmdline);
 
-  tmp_config_obj.set_virtual_disk_paths({FLAGS_composite_disk});
-
   tmp_config_obj.set_ramdisk_image_path(ramdisk_path);
   tmp_config_obj.set_vendor_ramdisk_image_path(vendor_ramdisk_path);
 
@@ -336,15 +323,6 @@
   tmp_config_obj.set_config_server_binary(
       vsoc::DefaultHostArtifactsPath("bin/config_server"));
 
-  instance.set_mobile_bridge_name(FLAGS_mobile_interface);
-  instance.set_mobile_tap_name(FLAGS_mobile_tap_name);
-
-  instance.set_wifi_tap_name(FLAGS_wifi_tap_name);
-
-  instance.set_vsock_guest_cid(FLAGS_vsock_guest_cid);
-
-  instance.set_uuid(FLAGS_uuid);
-
   tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
   tmp_config_obj.set_crosvm_binary(FLAGS_crosvm_binary);
   tmp_config_obj.set_console_forwarder_binary(
@@ -355,7 +333,6 @@
   tmp_config_obj.set_enable_vnc_server(FLAGS_start_vnc_server);
   tmp_config_obj.set_vnc_server_binary(
       vsoc::DefaultHostArtifactsPath("bin/vnc_server"));
-  instance.set_vnc_server_port(FLAGS_vnc_server_port);
 
   tmp_config_obj.set_enable_webrtc(FLAGS_start_webrtc);
   tmp_config_obj.set_webrtc_binary(
@@ -394,6 +371,41 @@
 
   tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
 
+  std::vector<int> instance_nums;
+  for (int i = 0; i < FLAGS_num_instances; i++) {
+    instance_nums.push_back(vsoc::GetInstance() + i);
+  }
+
+  for (const auto& num : instance_nums) {
+    auto instance = tmp_config_obj.ForInstance(num);
+    auto const_instance = const_cast<const vsoc::CuttlefishConfig&>(tmp_config_obj)
+        .ForInstance(num);
+    // Set this first so that calls to PerInstancePath below are correct
+    instance.set_instance_dir(FLAGS_instance_dir + "." + std::to_string(num));
+    instance.set_serial_number(FLAGS_serial_number + std::to_string(num));
+
+    instance.set_mobile_bridge_name(StrForInstance("cvd-mbr-", num));
+    instance.set_mobile_tap_name(StrForInstance("cvd-mtap-", num));
+
+    instance.set_wifi_tap_name(StrForInstance("cvd-wtap-", num));
+
+    instance.set_vsock_guest_cid(3 + num - 1);
+
+    instance.set_uuid(FLAGS_uuid);
+
+    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_device_title(FLAGS_device_title);
+
+    instance.set_virtual_disk_paths({const_instance.PerInstancePath("overlay.img")});
+  }
+
+  return tmp_config_obj;
+}
+
+bool SaveConfig(const vsoc::CuttlefishConfig& tmp_config_obj) {
   auto config_file = GetConfigFilePath(tmp_config_obj);
   auto config_link = vsoc::GetGlobalConfigFileLink();
   // Save the config object before starting any host process
@@ -456,15 +468,77 @@
   return ResolveInstanceFiles();
 }
 
-bool CleanPriorFiles() {
-  // Everything on the instance directory
-  std::string prior_files = FLAGS_instance_dir + "/*";
-  // Everything in the assembly directory
-  prior_files += " " + FLAGS_assembly_dir + "/*";
-  // The environment file
-  prior_files += " " + GetCuttlefishEnvPath();
-  // The global link to the config file
-  prior_files += " " + vsoc::GetGlobalConfigFileLink();
+std::string cpp_basename(const std::string& str) {
+  char* copy = strdup(str.c_str()); // basename may modify its argument
+  std::string ret(basename(copy));
+  free(copy);
+  return ret;
+}
+
+bool CleanPriorFiles(const std::string& path, const std::set<std::string>& preserving) {
+  if (preserving.count(cpp_basename(path))) {
+    LOG(INFO) << "Preserving: " << path;
+    return true;
+  }
+  struct stat statbuf;
+  if (lstat(path.c_str(), &statbuf) < 0) {
+    int error_num = errno;
+    if (error_num == ENOENT) {
+      return true;
+    } else {
+      LOG(ERROR) << "Could not stat \"" << path << "\": " << strerror(error_num);
+      return false;
+    }
+  }
+  if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+    LOG(INFO) << "Deleting: " << path;
+    if (unlink(path.c_str()) < 0) {
+      int error_num = errno;
+      LOG(ERROR) << "Could not unlink \"" << path << "\", error was " << strerror(error_num);
+      return false;
+    }
+    return true;
+  }
+  std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(path.c_str()), closedir);
+  if (!dir) {
+    int error_num = errno;
+    LOG(ERROR) << "Could not clean \"" << path << "\": error was " << strerror(error_num);
+    return false;
+  }
+  for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
+    std::string entity_name(entity->d_name);
+    if (entity_name == "." || entity_name == "..") {
+      continue;
+    }
+    std::string entity_path = path + "/" + entity_name;
+    if (!CleanPriorFiles(entity_path.c_str(), preserving)) {
+      return false;
+    }
+  }
+  if (rmdir(path.c_str()) < 0) {
+    if (!(errno == EEXIST || errno == ENOTEMPTY)) {
+      // If EEXIST or ENOTEMPTY, probably because a file was preserved
+      int error_num = errno;
+      LOG(ERROR) << "Could not rmdir \"" << path << "\", error was " << strerror(error_num);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool CleanPriorFiles(const std::vector<std::string>& paths, const std::set<std::string>& preserving) {
+  std::string prior_files;
+  for (auto path : paths) {
+    struct stat statbuf;
+    if (stat(path.c_str(), &statbuf) < 0 && errno != ENOENT) {
+      // If ENOENT, it doesn't exist yet, so there is no work to do'
+      int error_num = errno;
+      LOG(ERROR) << "Could not stat \"" << path << "\": " << strerror(error_num);
+      return false;
+    }
+    bool is_directory = (statbuf.st_mode & S_IFMT) == S_IFDIR;
+    prior_files += (is_directory ? (path + "/*") : path) + " ";
+  }
   LOG(INFO) << "Assuming prior files of " << prior_files;
   std::string lsof_cmd = "lsof -t " + prior_files + " >/dev/null 2>&1";
   int rval = std::system(lsof_cmd.c_str());
@@ -473,15 +547,31 @@
     LOG(ERROR) << "Clean aborted: files are in use";
     return false;
   }
-  std::string clean_command = "rm -rf " + prior_files;
-  rval = std::system(clean_command.c_str());
-  if (WEXITSTATUS(rval) != 0) {
-    LOG(ERROR) << "Remove of files failed";
-    return false;
+  for (const auto& path : paths) {
+    if (!CleanPriorFiles(path, preserving)) {
+      LOG(ERROR) << "Remove of file under \"" << path << "\" failed";
+      return false;
+    }
   }
   return true;
 }
 
+bool CleanPriorFiles(const vsoc::CuttlefishConfig& config, const std::set<std::string>& preserving) {
+  std::vector<std::string> paths = {
+    // Everything in the assembly directory
+    FLAGS_assembly_dir,
+    // The environment file
+    GetCuttlefishEnvPath(),
+    // The global link to the config file
+    vsoc::GetGlobalConfigFileLink(),
+  };
+  for (const auto& instance : config.Instances()) {
+    paths.push_back(instance.instance_dir());
+  }
+  paths.push_back(FLAGS_instance_dir);
+  return CleanPriorFiles(paths, preserving);
+}
+
 bool DecompressKernel(const std::string& src, const std::string& dst) {
   cvd::Command decomp_cmd(vsoc::DefaultHostArtifactsPath("bin/extract-vmlinux"));
   decomp_cmd.AddParameter(src);
@@ -537,23 +627,20 @@
   return partitions;
 }
 
-bool ShouldCreateCompositeDisk() {
-  if (FLAGS_vm_manager == vm_manager::CrosvmManager::name()) {
-    // The crosvm implementation is very fast to rebuild but also more brittle due to being split
-    // into multiple files. The QEMU implementation is slow to build, but completely self-contained
-    // at that point. Therefore, always rebuild on crosvm but check if it is necessary for QEMU.
-    return true;
-  }
-  auto composite_age = cvd::FileModificationTime(FLAGS_composite_disk);
+std::chrono::system_clock::time_point LastUpdatedInputDisk() {
+  std::chrono::system_clock::time_point ret;
   for (auto& partition : disk_config()) {
-    auto partition_age = cvd::FileModificationTime(partition.image_file_path);
-    if (partition_age >= composite_age) {
-      LOG(INFO) << "composite disk age was \"" << std::chrono::system_clock::to_time_t(composite_age) << "\", "
-                << "partition age was \"" << std::chrono::system_clock::to_time_t(partition_age) << "\"";
-      return true;
+    auto partition_mod_time = cvd::FileModificationTime(partition.image_file_path);
+    if (partition_mod_time > ret) {
+      ret = partition_mod_time;
     }
   }
-  return false;
+  return ret;
+}
+
+bool ShouldCreateCompositeDisk() {
+  auto composite_age = cvd::FileModificationTime(FLAGS_composite_disk);
+  return composite_age < LastUpdatedInputDisk();
 }
 
 bool ConcatRamdisks(const std::string& new_ramdisk_path, const std::string& ramdisk_a_path,
@@ -606,7 +693,7 @@
     }
     std::string header_path = config.AssemblyPath("gpt_header.img");
     std::string footer_path = config.AssemblyPath("gpt_footer.img");
-    create_composite_disk(disk_config(), header_path, footer_path, FLAGS_composite_disk);
+    CreateCompositeDisk(disk_config(), header_path, footer_path, FLAGS_composite_disk);
   } else {
     auto existing_size = cvd::FileSize(FLAGS_composite_disk);
     auto available_space = AvailableSpaceAtPath(FLAGS_composite_disk);
@@ -616,7 +703,7 @@
       LOG(ERROR) << "Got " << available_space;
       return false;
     }
-    aggregate_image(disk_config(), FLAGS_composite_disk);
+    AggregateImage(disk_config(), FLAGS_composite_disk);
   }
   return true;
 }
@@ -630,39 +717,72 @@
     exit(AssemblerExitCodes::kArgumentParsingError);
   }
 
-  // Clean up prior files before saving the config file (doing it after would
-  // delete it)
-  if (!CleanPriorFiles()) {
-    LOG(ERROR) << "Failed to clean prior files";
-    exit(AssemblerExitCodes::kPrioFilesCleanupError);
-  }
-  // Create assembly directory if it doesn't exist.
-  if (!cvd::DirectoryExists(FLAGS_assembly_dir.c_str())) {
-    LOG(INFO) << "Setting up " << FLAGS_assembly_dir;
-    if (mkdir(FLAGS_assembly_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
-      LOG(ERROR) << "Failed to create assembly directory: "
-                 << FLAGS_assembly_dir << ". Error: " << errno;
-      exit(AssemblerExitCodes::kAssemblyDirCreationError);
+  auto boot_img_unpacker =
+    cvd::BootImageUnpacker::FromImages(FLAGS_boot_image,
+                                      FLAGS_vendor_boot_image);
+  {
+    // The config object is created here, but only exists in memory until the
+    // SaveConfig line below. Don't launch cuttlefish subprocesses between these
+    // two operations, as those will assume they can read the config object from
+    // disk.
+    auto config = InitializeCuttlefishConfiguration(*boot_img_unpacker, fetcher_config);
+    std::set<std::string> preserving;
+    if (FLAGS_resume && ShouldCreateCompositeDisk()) {
+      LOG(WARNING) << "Requested resuming a previous session (the default behavior) "
+                   << "but the base images have changed under the overlay, making the "
+                   << "overlay incompatible. Wiping the overlay files.";
+    } else if (FLAGS_resume && !ShouldCreateCompositeDisk()) {
+      preserving.insert("overlay.img");
+      preserving.insert("gpt_header.img");
+      preserving.insert("gpt_footer.img");
+      preserving.insert("composite.img");
+      preserving.insert("access-kregistry");
     }
-  }
-  // Create instance directory if it doesn't exist.
-  if (!cvd::DirectoryExists(FLAGS_instance_dir.c_str())) {
-    LOG(INFO) << "Setting up " << FLAGS_instance_dir;
-    if (mkdir(FLAGS_instance_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
-      LOG(ERROR) << "Failed to create instance directory: "
-                 << FLAGS_instance_dir << ". Error: " << errno;
-      exit(AssemblerExitCodes::kInstanceDirCreationError);
+    if (!CleanPriorFiles(config, preserving)) {
+      LOG(ERROR) << "Failed to clean prior files";
+      exit(AssemblerExitCodes::kPrioFilesCleanupError);
+    }
+    // Create assembly directory if it doesn't exist.
+    if (!cvd::DirectoryExists(FLAGS_assembly_dir.c_str())) {
+      LOG(INFO) << "Setting up " << FLAGS_assembly_dir;
+      if (mkdir(FLAGS_assembly_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
+          && errno != EEXIST) {
+        LOG(ERROR) << "Failed to create assembly directory: "
+                  << FLAGS_assembly_dir << ". Error: " << errno;
+        exit(AssemblerExitCodes::kAssemblyDirCreationError);
+      }
+    }
+    for (const auto& instance : config.Instances()) {
+      // Create instance directory if it doesn't exist.
+      if (!cvd::DirectoryExists(instance.instance_dir().c_str())) {
+        LOG(INFO) << "Setting up " << FLAGS_instance_dir << ".N";
+        if (mkdir(instance.instance_dir().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
+            && errno != EEXIST) {
+          LOG(ERROR) << "Failed to create instance directory: "
+                    << FLAGS_instance_dir << ". Error: " << errno;
+          exit(AssemblerExitCodes::kInstanceDirCreationError);
+        }
+      }
+      auto internal_dir = instance.instance_dir() + "/" + vsoc::kInternalDirName;
+      if (!cvd::DirectoryExists(internal_dir)) {
+        if (mkdir(internal_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0
+           && errno != EEXIST) {
+          LOG(ERROR) << "Failed to create internal instance directory: "
+                    << internal_dir << ". Error: " << errno;
+          exit(AssemblerExitCodes::kInstanceDirCreationError);
+        }
+      }
+    }
+    if (!SaveConfig(config)) {
+      LOG(ERROR) << "Failed to initialize configuration";
+      exit(AssemblerExitCodes::kCuttlefishConfigurationInitError);
     }
   }
 
-  auto internal_dir = FLAGS_instance_dir + "/" + vsoc::kInternalDirName;
-  if (!cvd::DirectoryExists(internal_dir)) {
-    if (mkdir(internal_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) <
-        0) {
-      LOG(ERROR) << "Failed to create internal instance directory: "
-                 << internal_dir << ". Error: " << errno;
-      exit(AssemblerExitCodes::kInstanceDirCreationError);
-    }
+  std::string first_instance = FLAGS_instance_dir + "." + std::to_string(vsoc::GetInstance());
+  if (symlink(first_instance.c_str(), FLAGS_instance_dir.c_str()) < 0) {
+    LOG(ERROR) << "Could not symlink \"" << first_instance << "\" to \"" << FLAGS_instance_dir << "\"";
+    exit(cvd::kCuttlefishConfigurationInitError);
   }
 
   if (!cvd::FileHasContent(FLAGS_boot_image)) {
@@ -675,14 +795,6 @@
     exit(cvd::kCuttlefishConfigurationInitError);
   }
 
-  auto boot_img_unpacker =
-    cvd::BootImageUnpacker::FromImages(FLAGS_boot_image,
-                                       FLAGS_vendor_boot_image);
-
-  if (!InitializeCuttlefishConfiguration(*boot_img_unpacker, fetcher_config)) {
-    LOG(ERROR) << "Failed to initialize configuration";
-    exit(AssemblerExitCodes::kCuttlefishConfigurationInitError);
-  }
   // Do this early so that the config object is ready for anything that needs it
   auto config = vsoc::CuttlefishConfig::Get();
   if (!config) {
@@ -751,9 +863,10 @@
     CreateBlankImage(FLAGS_metadata_image, FLAGS_blank_metadata_image_mb, "none");
   }
 
-  if (!cvd::FileExists(config->ForDefaultInstance().access_kregistry_path())) {
-    CreateBlankImage(config->ForDefaultInstance().access_kregistry_path(), 1,
-                     "none", "64K");
+  for (const auto& instance : config->Instances()) {
+    if (!cvd::FileExists(instance.access_kregistry_path())) {
+      CreateBlankImage(instance.access_kregistry_path(), 1, "none", "64K");
+    }
   }
 
   if (SuperImageNeedsRebuilding(fetcher_config, *config)) {
@@ -769,11 +882,26 @@
     }
   }
 
-  // Check that the files exist
-  for (const auto& file : config->virtual_disk_paths()) {
-    if (!file.empty() && !cvd::FileHasContent(file.c_str())) {
-      LOG(ERROR) << "File not found: " << file;
-      exit(cvd::kCuttlefishConfigurationInitError);
+  for (auto instance : config->Instances()) {
+    auto overlay_path = instance.PerInstancePath("overlay.img");
+    if (!cvd::FileExists(overlay_path) || ShouldCreateCompositeDisk() || !FLAGS_resume
+        || cvd::FileModificationTime(overlay_path) < cvd::FileModificationTime(FLAGS_composite_disk)) {
+      if (FLAGS_resume) {
+        LOG(WARNING) << "Requested to continue an existing session, but the overlay was "
+                     << "newer than its underlying composite disk. Wiping the overlay.";
+      }
+      CreateQcowOverlay(config->crosvm_binary(), FLAGS_composite_disk, overlay_path);
+      CreateBlankImage(instance.access_kregistry_path(), 1, "none", "64K");
+    }
+  }
+
+  for (auto instance : config->Instances()) {
+    // Check that the files exist
+    for (const auto& file : instance.virtual_disk_paths()) {
+      if (!file.empty() && !cvd::FileHasContent(file.c_str())) {
+        LOG(ERROR) << "File not found: " << file;
+        exit(cvd::kCuttlefishConfigurationInitError);
+      }
     }
   }
 
diff --git a/host/commands/assemble_cvd/image_aggregator.cc b/host/commands/assemble_cvd/image_aggregator.cc
index 17fe9db..eac6a2e 100644
--- a/host/commands/assemble_cvd/image_aggregator.cc
+++ b/host/commands/assemble_cvd/image_aggregator.cc
@@ -16,6 +16,11 @@
 
 #include "host/commands/assemble_cvd/image_aggregator.h"
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+
 #include <fstream>
 #include <string>
 #include <vector>
@@ -23,6 +28,7 @@
 #include <glog/logging.h>
 #include <json/json.h>
 #include <google/protobuf/text_format.h>
+#include <sparse/sparse.h>
 
 #include "common/libs/fs/shared_buf.h"
 #include "common/libs/fs/shared_fd.h"
@@ -38,15 +44,36 @@
 
 const std::string BPTTOOL_FILE_PATH = "bin/cf_bpttool";
 
-Json::Value bpttool_input(const std::vector<ImagePartition>& partitions) {
+Json::Value BpttoolInput(const std::vector<ImagePartition>& partitions) {
   std::vector<off_t> file_sizes;
   off_t total_size = 20 << 20; // 20 MB for padding
   for (auto& partition : partitions) {
-    off_t partition_file_size = cvd::FileSize(partition.image_file_path);
-    if (partition_file_size == 0) {
-      LOG(FATAL) << "Expected partition file \"" << partition.image_file_path
-                 << "\" but it was missing";
+    LOG(INFO) << "Examining " << partition.label;
+    auto file = cvd::SharedFD::Open(partition.image_file_path.c_str(), O_RDONLY);
+    if (!file->IsOpen()) {
+      LOG(FATAL) << "Could not open \"" << partition.image_file_path
+                 << "\": " << file->StrError();
+      break;
     }
+    int fd = file->UNMANAGED_Dup();
+    auto sparse = sparse_file_import(fd, /* verbose */ false, /* crc */ false);
+    off_t partition_file_size = 0;
+    if (sparse) {
+      partition_file_size = sparse_file_len(sparse, /* sparse */ false,
+                                            /* crc */ true);
+      sparse_file_destroy(sparse);
+      close(fd);
+      LOG(INFO) << "was sparse";
+    } else {
+      partition_file_size = cvd::FileSize(partition.image_file_path);
+      if (partition_file_size == 0) {
+        LOG(FATAL) << "Could not get file size of \"" << partition.image_file_path
+                  << "\"";
+        break;
+      }
+      LOG(INFO) << "was not sparse";
+    }
+    LOG(INFO) << "size was " << partition_file_size;
     total_size += partition_file_size;
     file_sizes.push_back(partition_file_size);
   }
@@ -65,7 +92,7 @@
   return bpttool_input_json;
 }
 
-std::string create_file(size_t len) {
+std::string CreateFile(size_t len) {
   char file_template[] = "/tmp/diskXXXXXX";
   int fd = mkstemp(file_template);
   if (fd < 0) {
@@ -97,7 +124,7 @@
   for (auto& bpt_partition: bpt_file["partitions"]) {
     if (bpt_partition["offset"].asUInt64() != previous_end) {
       ComponentDisk* component = disk.add_component_disks();
-      component->set_file_path(create_file(bpt_partition["offset"].asUInt64() - previous_end));
+      component->set_file_path(CreateFile(bpt_partition["offset"].asUInt64() - previous_end));
       component->set_offset(previous_end);
     }
     ComponentDisk* component = disk.add_component_disks();
@@ -113,7 +140,7 @@
   size_t footer_start = bpt_file["settings"]["disk_size"].asUInt64() - GPT_FOOTER_SIZE;
   if (footer_start != previous_end) {
     ComponentDisk* component = disk.add_component_disks();
-    component->set_file_path(create_file(footer_start - previous_end));
+    component->set_file_path(CreateFile(footer_start - previous_end));
     component->set_offset(previous_end);
   }
   ComponentDisk* footer = disk.add_component_disks();
@@ -123,7 +150,7 @@
   return disk;
 }
 
-cvd::SharedFD json_to_fd(const Json::Value& json) {
+cvd::SharedFD JsonToFd(const Json::Value& json) {
   Json::FastWriter json_writer;
   std::string json_string = json_writer.write(json);
   cvd::SharedFD pipe[2];
@@ -137,7 +164,7 @@
   return pipe[0];
 }
 
-Json::Value fd_to_json(cvd::SharedFD fd) {
+Json::Value FdToJson(cvd::SharedFD fd) {
   std::string contents;
   cvd::ReadAll(fd, &contents);
   Json::Reader reader;
@@ -148,7 +175,7 @@
   return json;
 }
 
-cvd::SharedFD bpttool_make_table(const cvd::SharedFD& input) {
+cvd::SharedFD BpttoolMakeTable(const cvd::SharedFD& input) {
   auto bpttool_path = vsoc::DefaultHostArtifactsPath(BPTTOOL_FILE_PATH);
   cvd::Command bpttool_cmd(bpttool_path);
   bpttool_cmd.AddParameter("make_table");
@@ -165,7 +192,7 @@
   return output_pipe[0];
 }
 
-cvd::SharedFD bpttool_make_partition_table(cvd::SharedFD input) {
+cvd::SharedFD BpttoolMakePartitionTable(cvd::SharedFD input) {
   auto bpttool_path = vsoc::DefaultHostArtifactsPath(BPTTOOL_FILE_PATH);
   cvd::Command bpttool_cmd(bpttool_path);
   bpttool_cmd.AddParameter("make_table");
@@ -203,8 +230,8 @@
   }
 }
 
-void bpttool_make_disk_image(const std::vector<ImagePartition>& partitions,
-                             cvd::SharedFD table, const std::string& output) {
+void BptToolMakeDiskImage(const std::vector<ImagePartition>& partitions,
+                          cvd::SharedFD table, const std::string& output) {
   auto bpttool_path = vsoc::DefaultHostArtifactsPath(BPTTOOL_FILE_PATH);
   cvd::Command bpttool_cmd(bpttool_path);
   bpttool_cmd.AddParameter("make_disk_image");
@@ -221,28 +248,77 @@
   }
 }
 
+void DeAndroidSparse(const std::vector<ImagePartition>& partitions) {
+  for (const auto& partition : partitions) {
+    auto file = cvd::SharedFD::Open(partition.image_file_path.c_str(), O_RDONLY);
+    if (!file->IsOpen()) {
+      LOG(FATAL) << "Could not open \"" << partition.image_file_path
+                  << "\": " << file->StrError();
+      break;
+    }
+    int fd = file->UNMANAGED_Dup();
+    auto sparse = sparse_file_import(fd, /* verbose */ false, /* crc */ false);
+    if (!sparse) {
+      close(fd);
+      continue;
+    }
+    LOG(INFO) << "Desparsing " << partition.image_file_path;
+    std::string out_file_name = partition.image_file_path + ".desparse";
+    auto out_file = cvd::SharedFD::Open(out_file_name.c_str(), O_RDWR | O_CREAT | O_TRUNC,
+                                        S_IRUSR | S_IWUSR | S_IRGRP);
+    int write_fd = out_file->UNMANAGED_Dup();
+    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;
+    }
+    close(write_fd);
+    if (rename(out_file_name.c_str(), partition.image_file_path.c_str()) < 0) {
+      int error_num = errno;
+      LOG(FATAL) << "Could not move \"" << out_file_name << "\" to \""
+                 << partition.image_file_path << "\": " << strerror(error_num);
+    }
+    sparse_file_destroy(sparse);
+    close(fd);
+  }
+}
+
 } // namespace
 
-void aggregate_image(const std::vector<ImagePartition>& partitions,
-                     const std::string& output_path) {
-  auto bpttool_input_json = bpttool_input(partitions);
-  auto input_json_fd = json_to_fd(bpttool_input_json);
-  auto table_fd = bpttool_make_table(input_json_fd);
-  bpttool_make_disk_image(partitions, table_fd, output_path);
+void AggregateImage(const std::vector<ImagePartition>& partitions,
+                    const std::string& output_path) {
+  DeAndroidSparse(partitions);
+  auto bpttool_input_json = BpttoolInput(partitions);
+  auto input_json_fd = JsonToFd(bpttool_input_json);
+  auto table_fd = BpttoolMakeTable(input_json_fd);
+  BptToolMakeDiskImage(partitions, table_fd, output_path);
 };
 
-void create_composite_disk(std::vector<ImagePartition> partitions,
-                           const std::string& header_file,
-                           const std::string& footer_file,
-                           const std::string& output_path) {
-  auto bpttool_input_json = bpttool_input(partitions);
-  auto table_fd = bpttool_make_table(json_to_fd(bpttool_input_json));
-  auto table = fd_to_json(table_fd);
-  auto partition_table_fd = bpttool_make_partition_table(json_to_fd(bpttool_input_json));
+void CreateCompositeDisk(std::vector<ImagePartition> partitions,
+                         const std::string& header_file,
+                         const std::string& footer_file,
+                         const std::string& output_composite_path) {
+  auto bpttool_input_json = BpttoolInput(partitions);
+  auto table_fd = BpttoolMakeTable(JsonToFd(bpttool_input_json));
+  auto table = FdToJson(table_fd);
+  auto partition_table_fd = BpttoolMakePartitionTable(JsonToFd(bpttool_input_json));
   CreateGptFiles(partition_table_fd, header_file, footer_file);
   auto composite_proto = MakeCompositeDiskSpec(table, partitions, header_file, footer_file);
-  std::ofstream output(output_path.c_str(), std::ios::binary | std::ios::trunc);
-  output << "composite_disk\x1d";
-  composite_proto.SerializeToOstream(&output);
-  output.flush();
+  std::ofstream composite(output_composite_path.c_str(), std::ios::binary | std::ios::trunc);
+  composite << "composite_disk\x1d";
+  composite_proto.SerializeToOstream(&composite);
+  composite.flush();
+}
+
+void CreateQcowOverlay(const std::string& crosvm_path,
+                       const std::string& backing_file,
+                       const std::string& output_overlay_path) {
+  cvd::Command crosvm_qcow2_cmd(crosvm_path);
+  crosvm_qcow2_cmd.AddParameter("create_qcow2");
+  crosvm_qcow2_cmd.AddParameter("--backing_file=", backing_file);
+  crosvm_qcow2_cmd.AddParameter(output_overlay_path);
+  int success = crosvm_qcow2_cmd.Start().Wait();
+  if (success != 0) {
+    LOG(FATAL) << "Unable to run crosvm create_qcow2. Exited with status " << success;
+  }
 }
diff --git a/host/commands/assemble_cvd/image_aggregator.h b/host/commands/assemble_cvd/image_aggregator.h
index a4394e8..d27931b 100644
--- a/host/commands/assemble_cvd/image_aggregator.h
+++ b/host/commands/assemble_cvd/image_aggregator.h
@@ -24,9 +24,12 @@
   std::string image_file_path;
 };
 
-void aggregate_image(const std::vector<ImagePartition>& partitions,
-                     const std::string& output_path);
-void create_composite_disk(std::vector<ImagePartition> partitions,
-                           const std::string& header_file,
-                           const std::string& footer_file,
-                           const std::string& output_path);
+void AggregateImage(const std::vector<ImagePartition>& partitions,
+                    const std::string& output_path);
+void CreateCompositeDisk(std::vector<ImagePartition> partitions,
+                         const std::string& header_file,
+                         const std::string& footer_file,
+                         const std::string& output_composite_path);
+void CreateQcowOverlay(const std::string& crosvm_path,
+                       const std::string& backing_file,
+                       const std::string& output_overlay_path);
diff --git a/host/commands/launch/launch_cvd.cc b/host/commands/launch/launch_cvd.cc
index 9d3b108..6cc7c93 100644
--- a/host/commands/launch/launch_cvd.cc
+++ b/host/commands/launch/launch_cvd.cc
@@ -39,6 +39,7 @@
  */
 DEFINE_bool(run_file_discovery, true,
             "Whether to run file discovery or get input files from stdin.");
+DEFINE_int32(num_instances, 1, "Number of Android guests to launch");
 
 namespace {
 
@@ -95,8 +96,8 @@
 
   gflags::HandleCommandLineHelpFlags();
 
-  cvd::SharedFD assembler_stdout, runner_stdin;
-  cvd::SharedFD::Pipe(&runner_stdin, &assembler_stdout);
+  cvd::SharedFD assembler_stdout, assembler_stdout_capture;
+  cvd::SharedFD::Pipe(&assembler_stdout_capture, &assembler_stdout);
 
   cvd::SharedFD launcher_report, assembler_stdin;
   bool should_generate_report = FLAGS_run_file_discovery;
@@ -109,13 +110,18 @@
   auto assemble_proc = StartAssembler(std::move(assembler_stdin),
                                       std::move(assembler_stdout),
                                       forwarder.ArgvForSubprocess(kAssemblerBin));
-  auto run_proc = StartRunner(std::move(runner_stdin),
-                              forwarder.ArgvForSubprocess(kRunnerBin));
 
   if (should_generate_report) {
     WriteFiles(AvailableFilesReport(), std::move(launcher_report));
   }
 
+  std::string assembler_output;
+  if (cvd::ReadAll(assembler_stdout_capture, &assembler_output) < 0) {
+    int error_num = errno;
+    LOG(ERROR) << "Read error getting output from assemble_cvd: " << strerror(error_num);
+    return -1;
+  }
+
   auto assemble_ret = assemble_proc.Wait();
   if (assemble_ret != 0) {
     LOG(ERROR) << "assemble_cvd returned " << assemble_ret;
@@ -124,11 +130,32 @@
     LOG(INFO) << "assemble_cvd exited successfully.";
   }
 
-  auto run_ret = run_proc.Wait();
-  if (run_ret != 0) {
-    LOG(ERROR) << "run_cvd returned " << run_ret;
-  } else {
-    LOG(INFO) << "run_cvd exited successfully.";
+  std::vector<cvd::Subprocess> runners;
+  for (int i = 0; i < FLAGS_num_instances; i++) {
+    cvd::SharedFD runner_stdin_in, runner_stdin_out;
+    cvd::SharedFD::Pipe(&runner_stdin_out, &runner_stdin_in);
+    std::string instance_name = std::to_string(i + vsoc::GetInstance());
+    setenv("CUTTLEFISH_INSTANCE", instance_name.c_str(), /* overwrite */ 1);
+
+    auto run_proc = StartRunner(std::move(runner_stdin_out),
+                                forwarder.ArgvForSubprocess(kRunnerBin));
+    runners.push_back(std::move(run_proc));
+    if (cvd::WriteAll(runner_stdin_in, assembler_output) < 0) {
+      int error_num = errno;
+      LOG(ERROR) << "Could not write to run_cvd: " << strerror(error_num);
+      return -1;
+    }
   }
-  return run_ret;
+
+  bool run_cvd_failure = false;
+  for (auto& run_proc : runners) {
+    auto run_ret = run_proc.Wait();
+    if (run_ret != 0) {
+      run_cvd_failure = true;
+      LOG(ERROR) << "run_cvd returned " << run_ret;
+    } else {
+      LOG(INFO) << "run_cvd exited successfully.";
+    }
+  }
+  return run_cvd_failure ? -1 : 0;
 }
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index 3c92959..dc2e108 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -110,7 +110,8 @@
   cmd->AddParameter("-keyboard_fd=", keyboard_server);
 
   cvd::SharedFD frames_server;
-  if (config.gpu_mode() == vsoc::kGpuModeDrmVirgl) {
+  if (config.gpu_mode() == vsoc::kGpuModeDrmVirgl ||
+      config.gpu_mode() == vsoc::kGpuModeGfxStream) {
     frames_server = CreateUnixInputServer(instance.frames_socket_path());
   } else {
     frames_server = cvd::SharedFD::VsockServer(SOCK_STREAM);
diff --git a/host/commands/stop_cvd/main.cc b/host/commands/stop_cvd/main.cc
index 7d425a0..65dbeb7 100644
--- a/host/commands/stop_cvd/main.cc
+++ b/host/commands/stop_cvd/main.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <dirent.h>
 #include <inttypes.h>
 #include <limits.h>
 #include <stdio.h>
@@ -35,6 +36,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/strings.h>
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 
@@ -50,23 +52,57 @@
              "command. A value of zero means wait indefinetly");
 
 namespace {
+
+std::set<std::string> FallbackPaths() {
+  std::set<std::string> paths;
+  std::string parent_path = cvd::StringFromEnv("HOME", ".");
+  paths.insert(parent_path + "/cuttlefish_assembly");
+  paths.insert(parent_path + "/cuttlefish_assembly/*");
+
+  std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(parent_path.c_str()), closedir);
+  for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
+    std::string subdir(entity->d_name);
+    if (!android::base::StartsWith(subdir, "cuttlefish_runtime.")) {
+      continue;
+    }
+    auto instance_dir = parent_path + "/" + subdir;
+    // Add the instance directory
+    paths.insert(instance_dir);
+    // Add files in instance dir
+    paths.insert(instance_dir + "/*");
+    // Add files in the tombstone directory
+    paths.insert(instance_dir + "/tombstones/*");
+    // Add files in the internal directory
+    paths.insert(instance_dir + "/" + std::string(vsoc::kInternalDirName) + "/*");
+  }
+  return paths;
+}
+
+std::set<std::string> PathsForInstance(const vsoc::CuttlefishConfig& config,
+                                       const vsoc::CuttlefishConfig::InstanceSpecific instance) {
+  return {
+    config.assembly_dir(),
+    config.assembly_dir() + "/*",
+    instance.instance_dir(),
+    instance.PerInstancePath("*"),
+    instance.PerInstancePath("tombstones"),
+    instance.PerInstancePath("tombstones/*"),
+    instance.instance_internal_dir(),
+    instance.PerInstanceInternalPath("*"),
+  };
+}
+
 // Gets a set of the possible process groups of a previous launch
-std::set<pid_t> GetCandidateProcessGroups() {
-  std::string cmd = "lsof -t 2>/dev/null";
-  // Add the instance directory
-  auto instance_dir = cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
-  cmd += " " + instance_dir;
-  // Add files in instance dir
-  cmd += " " + instance_dir + "/*";
-  // Add files in the tombstone directory
-  cmd += " " + instance_dir + "/tombstones/*";
-  // Add files in the internal directory
-  cmd += ((" " + instance_dir + "/") + vsoc::kInternalDirName) + "/*";
-  // Add the shared memory file
-  cmd += " " + vsoc::ForCurrentInstance("/dev/shm/cvd-");
-  std::shared_ptr<FILE> cmd_out(popen(cmd.c_str(), "r"), pclose);
+std::set<pid_t> GetCandidateProcessGroups(const std::set<std::string>& paths) {
+  std::stringstream cmd;
+  cmd << "lsof -t 2>/dev/null";
+  for (const auto& path : paths) {
+    cmd << " " << path;
+  }
+  std::string cmd_str = cmd.str();
+  std::shared_ptr<FILE> cmd_out(popen(cmd_str.c_str(), "r"), pclose);
   if (!cmd_out) {
-    LOG(ERROR) << "Unable to execute '" << cmd << "': " << strerror(errno);
+    LOG(ERROR) << "Unable to execute '" << cmd_str << "': " << strerror(errno);
     return {};
   }
   int64_t pid;
@@ -85,10 +121,10 @@
   return ret;
 }
 
-int FallBackStop() {
+int FallBackStop(const std::set<std::string>& paths) {
   auto exit_code = 1; // Having to fallback is an error
 
-  auto process_groups = GetCandidateProcessGroups();
+  auto process_groups = GetCandidateProcessGroups(paths);
   for (auto pgid: process_groups) {
     LOG(INFO) << "Sending SIGKILL to process group " << pgid;
     auto retval = killpg(pgid, SIGKILL);
@@ -101,37 +137,26 @@
 
   return exit_code;
 }
-}  // anonymous namespace
 
-int main(int argc, char** argv) {
-  ::android::base::InitLogging(argv, android::base::StderrLogger);
-  google::ParseCommandLineFlags(&argc, &argv, true);
-
-  auto config = vsoc::CuttlefishConfig::Get();
-  auto instance = config->ForDefaultInstance();
-  if (!config) {
-    LOG(ERROR) << "Failed to obtain config object";
-    return FallBackStop();
-  }
-
+bool CleanStopInstance(const vsoc::CuttlefishConfig::InstanceSpecific& instance) {
   auto monitor_path = instance.launcher_monitor_socket_path();
   if (monitor_path.empty()) {
     LOG(ERROR) << "No path to launcher monitor found";
-    return FallBackStop();
+    return false;
   }
   auto monitor_socket = cvd::SharedFD::SocketLocalClient(monitor_path.c_str(),
                                                          false, SOCK_STREAM);
   if (!monitor_socket->IsOpen()) {
     LOG(ERROR) << "Unable to connect to launcher monitor at " << monitor_path
                << ": " << monitor_socket->StrError();
-    return FallBackStop();
+    return false;
   }
   auto request = cvd::LauncherAction::kStop;
   auto bytes_sent = monitor_socket->Send(&request, sizeof(request), 0);
   if (bytes_sent < 0) {
     LOG(ERROR) << "Error sending launcher monitor the stop command: "
                << monitor_socket->StrError();
-    return FallBackStop();
+    return false;
   }
   // Perform a select with a timeout to guard against launcher hanging
   cvd::SharedFDSet read_set;
@@ -142,24 +167,53 @@
   if (selected < 0){
     LOG(ERROR) << "Failed communication with the launcher monitor: "
                << strerror(errno);
-    return FallBackStop();
+    return false;
   }
   if (selected == 0) {
     LOG(ERROR) << "Timeout expired waiting for launcher monitor to respond";
-    return FallBackStop();
+    return false;
   }
   cvd::LauncherResponse response;
   auto bytes_recv = monitor_socket->Recv(&response, sizeof(response), 0);
   if (bytes_recv < 0) {
     LOG(ERROR) << "Error receiving response from launcher monitor: "
                << monitor_socket->StrError();
-    return FallBackStop();
+    return false;
   }
   if (response != cvd::LauncherResponse::kSuccess) {
     LOG(ERROR) << "Received '" << static_cast<char>(response)
                << "' response from launcher monitor";
-    return FallBackStop();
+    return false;
   }
-  LOG(INFO) << "Successfully stopped device";
+  LOG(INFO) << "Successfully stopped device " << instance.adb_ip_and_port();
+  return true;
+}
+
+int StopInstance(const vsoc::CuttlefishConfig& config,
+                 const vsoc::CuttlefishConfig::InstanceSpecific& instance) {
+  bool res = CleanStopInstance(instance);
+  if (!res) {
+    return FallBackStop(PathsForInstance(config, instance));
+  }
   return 0;
 }
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto config = vsoc::CuttlefishConfig::Get();
+  if (!config) {
+    LOG(ERROR) << "Failed to obtain config object";
+    return FallBackStop(FallbackPaths());
+  }
+
+  int ret = 0;
+  for (const auto& instance : config->Instances()) {
+    ret |= StopInstance(*config, instance);
+  }
+
+  return ret;
+}
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 818cb6d..c133a62 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -163,6 +163,7 @@
 
 const char* const kGpuModeGuestSwiftshader = "guest_swiftshader";
 const char* const kGpuModeDrmVirgl = "drm_virgl";
+const char* const kGpuModeGfxStream = "gfxstream";
 
 std::string DefaultEnvironmentPath(const char* environment_key,
                                    const char* default_value,
@@ -338,21 +339,21 @@
   SetPath(kVendorRamdiskImagePath, vendor_ramdisk_image_path);
 }
 
-std::vector<std::string> CuttlefishConfig::virtual_disk_paths() const {
+std::vector<std::string> CuttlefishConfig::InstanceSpecific::virtual_disk_paths() const {
   std::vector<std::string> virtual_disks;
-  auto virtual_disks_json_obj = (*dictionary_)[kVirtualDiskPaths];
+  auto virtual_disks_json_obj = (*Dictionary())[kVirtualDiskPaths];
   for (const auto& disk : virtual_disks_json_obj) {
     virtual_disks.push_back(disk.asString());
   }
   return virtual_disks;
 }
-void CuttlefishConfig::set_virtual_disk_paths(
+void CuttlefishConfig::MutableInstanceSpecific::set_virtual_disk_paths(
     const std::vector<std::string>& virtual_disk_paths) {
   Json::Value virtual_disks_json_obj(Json::arrayValue);
   for (const auto& arg : virtual_disk_paths) {
     virtual_disks_json_obj.append(arg);
   }
-  (*dictionary_)[kVirtualDiskPaths] = virtual_disks_json_obj;
+  (*Dictionary())[kVirtualDiskPaths] = virtual_disks_json_obj;
 }
 
 std::string CuttlefishConfig::InstanceSpecific::kernel_log_pipe_name() const {
@@ -843,7 +844,10 @@
 CuttlefishConfig::CuttlefishConfig() : dictionary_(new Json::Value()) {}
 // Can't use '= default' on the header because the compiler complains of
 // Json::Value being an incomplete type
-CuttlefishConfig::~CuttlefishConfig() {}
+CuttlefishConfig::~CuttlefishConfig() = default;
+
+CuttlefishConfig::CuttlefishConfig(CuttlefishConfig&&) = default;
+CuttlefishConfig& CuttlefishConfig::operator=(CuttlefishConfig&&) = default;
 
 bool CuttlefishConfig::LoadFromFile(const char* file) {
   auto real_file_path = cvd::AbsolutePath(file);
@@ -894,14 +898,27 @@
   return ForCurrentInstance("cvd-");
 }
 
-CuttlefishConfig::MutableInstanceSpecific CuttlefishConfig::ForDefaultInstance() {
-  return MutableInstanceSpecific(this, std::to_string(GetInstance()));
+CuttlefishConfig::MutableInstanceSpecific CuttlefishConfig::ForInstance(int num) {
+  return MutableInstanceSpecific(this, std::to_string(num));
+}
+
+CuttlefishConfig::InstanceSpecific CuttlefishConfig::ForInstance(int num) const {
+  return InstanceSpecific(this, std::to_string(num));
 }
 
 CuttlefishConfig::InstanceSpecific CuttlefishConfig::ForDefaultInstance() const {
   return InstanceSpecific(this, std::to_string(GetInstance()));
 }
 
+std::vector<CuttlefishConfig::InstanceSpecific> CuttlefishConfig::Instances() const {
+  const auto& json = (*dictionary_)[kInstances];
+  std::vector<CuttlefishConfig::InstanceSpecific> instances;
+  for (const auto& name : json.getMemberNames()) {
+    instances.push_back(CuttlefishConfig::InstanceSpecific(this, name));
+  }
+  return instances;
+}
+
 int GetInstance() {
   static int instance_id = InstanceFromEnvironment();
   return instance_id;
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 2ee6135..f78327d 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -18,6 +18,7 @@
 #include <memory>
 #include <string>
 #include <set>
+#include <vector>
 
 namespace Json {
 class Value;
@@ -51,7 +52,9 @@
   static const CuttlefishConfig* Get();
 
   CuttlefishConfig();
+  CuttlefishConfig(CuttlefishConfig&&);
   ~CuttlefishConfig();
+  CuttlefishConfig& operator=(CuttlefishConfig&&);
 
   // Saves the configuration object in a file, it can then be read in other
   // processes by passing the --config_file option.
@@ -135,9 +138,6 @@
   void set_vendor_ramdisk_image_path(const std::string&
     vendor_ramdisk_image_path);
 
-  std::vector<std::string> virtual_disk_paths() const;
-  void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);
-
   bool deprecated_boot_completed() const;
   void set_deprecated_boot_completed(bool deprecated_boot_completed);
 
@@ -259,21 +259,22 @@
   class InstanceSpecific;
   class MutableInstanceSpecific;
 
-  MutableInstanceSpecific ForDefaultInstance();
+  MutableInstanceSpecific ForInstance(int instance_num);
+  InstanceSpecific ForInstance(int instance_num) const;
   InstanceSpecific ForDefaultInstance() const;
 
+  std::vector<InstanceSpecific> Instances() const;
+
   // A view into an existing CuttlefishConfig object for a particular instance.
   class InstanceSpecific {
     const CuttlefishConfig* config_;
     std::string id_;
+    friend InstanceSpecific CuttlefishConfig::ForInstance(int num) const;
     friend InstanceSpecific CuttlefishConfig::ForDefaultInstance() const;
+    friend std::vector<InstanceSpecific> CuttlefishConfig::Instances() const;
 
     InstanceSpecific(const CuttlefishConfig* config, const std::string& id)
         : config_(config), id_(id) {}
-    InstanceSpecific(const InstanceSpecific&) = delete;
-    InstanceSpecific(InstanceSpecific&&) = delete;
-    InstanceSpecific& operator=(const InstanceSpecific&) = delete;
-    InstanceSpecific& operator=(InstanceSpecific&&) = delete;
 
     Json::Value* Dictionary();
     const Json::Value* Dictionary() const;
@@ -290,6 +291,7 @@
     int vsock_guest_cid() const;
     std::string uuid() const;
     std::string instance_name() const;
+    std::vector<std::string> virtual_disk_paths() const;
 
     // Returns the path to a file with the given name in the instance directory..
     std::string PerInstancePath(const char* file_name) const;
@@ -322,14 +324,10 @@
   class MutableInstanceSpecific {
     CuttlefishConfig* config_;
     std::string id_;
-    friend MutableInstanceSpecific CuttlefishConfig::ForDefaultInstance();
+    friend MutableInstanceSpecific CuttlefishConfig::ForInstance(int num);
 
     MutableInstanceSpecific(CuttlefishConfig* config, const std::string& id)
         : config_(config), id_(id) {}
-    MutableInstanceSpecific(const MutableInstanceSpecific&) = delete;
-    MutableInstanceSpecific(MutableInstanceSpecific&&) = delete;
-    MutableInstanceSpecific& operator=(const MutableInstanceSpecific&) = delete;
-    MutableInstanceSpecific& operator=(MutableInstanceSpecific&&) = delete;
 
     Json::Value* Dictionary();
   public:
@@ -344,14 +342,12 @@
     void set_vsock_guest_cid(int vsock_guest_cid);
     void set_uuid(const std::string& uuid);
     void set_instance_dir(const std::string& instance_dir);
+    void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);
   };
 
  private:
   std::unique_ptr<Json::Value> dictionary_;
 
-  InstanceSpecific ForInstance(int instance_num);
-  const InstanceSpecific ForInstance(int instance_num) const;
-
   void SetPath(const std::string& key, const std::string& path);
   bool LoadFromFile(const char* file);
   static CuttlefishConfig* BuildConfigImpl();
@@ -390,4 +386,5 @@
 // GPU modes
 extern const char* const kGpuModeGuestSwiftshader;
 extern const char* const kGpuModeDrmVirgl;
+extern const char* const kGpuModeGfxStream;
 }  // namespace vsoc
diff --git a/host/libs/screen_connector/screen_connector.cpp b/host/libs/screen_connector/screen_connector.cpp
index 906435a..39c0fa3 100644
--- a/host/libs/screen_connector/screen_connector.cpp
+++ b/host/libs/screen_connector/screen_connector.cpp
@@ -26,7 +26,8 @@
 
 ScreenConnector* ScreenConnector::Get(int frames_fd) {
   auto config = vsoc::CuttlefishConfig::Get();
-  if (config->gpu_mode() == vsoc::kGpuModeDrmVirgl) {
+  if (config->gpu_mode() == vsoc::kGpuModeDrmVirgl ||
+      config->gpu_mode() == vsoc::kGpuModeGfxStream) {
     return new WaylandScreenConnector(frames_fd);
   } else if (config->gpu_mode() == vsoc::kGpuModeGuestSwiftshader) {
     return new SocketBasedScreenConnector(frames_fd);
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
index 5d88b3f..3eea194 100755
--- a/host/libs/vm_manager/cf_qemu.sh
+++ b/host/libs/vm_manager/cf_qemu.sh
@@ -105,7 +105,7 @@
     bootindex=""
   fi
   args+=(
-    -drive "file=${virtual_disk},format=raw,if=none,id=drive-virtio-disk${virtual_disk_index},aio=threads"
+    -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))
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 5674a21..63dd3ed 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -84,6 +84,15 @@
         "androidboot.hardware.vulkan=pastel",
     };
   }
+  if (gpu_mode == vsoc::kGpuModeGfxStream) {
+    return {
+        "androidboot.hardware.gralloc=minigbm",
+        "androidboot.hardware.hwcomposer=ranchu",
+        "androidboot.hardware.egl=emulation",
+        "androidboot.hardware.vulkan=ranchu",
+        "androidboot.hardware.gltransport=virtio-gpu-pipe",
+    };
+  }
   return {};
 }
 
@@ -108,8 +117,12 @@
   });
   crosvm_cmd.AddParameter("run");
 
-  if (config_->gpu_mode() == vsoc::kGpuModeDrmVirgl) {
-    crosvm_cmd.AddParameter("--gpu=",
+  auto gpu_mode = config_->gpu_mode();
+
+  if (gpu_mode == vsoc::kGpuModeDrmVirgl ||
+      gpu_mode == vsoc::kGpuModeGfxStream) {
+    crosvm_cmd.AddParameter(gpu_mode == vsoc::kGpuModeGfxStream ?
+                                "--gpu=gfxstream," : "--gpu=",
                             "width=", config_->x_res(), ",",
                             "height=", config_->y_res(), ",",
                             "egl=true,surfaceless=true,glx=false,gles=false");
@@ -122,7 +135,7 @@
   crosvm_cmd.AddParameter("--mem=", config_->memory_mb());
   crosvm_cmd.AddParameter("--cpus=", config_->cpus());
   crosvm_cmd.AddParameter("--params=", kernel_cmdline_);
-  for (const auto& disk : config_->virtual_disk_paths()) {
+  for (const auto& disk : instance.virtual_disk_paths()) {
     crosvm_cmd.AddParameter("--rwdisk=", disk);
   }
   crosvm_cmd.AddParameter("--socket=", GetControlSocketPath(config_));
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 643ffc5..af15f0d 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -140,7 +140,7 @@
   LogAndSetEnv("gdb_flag", config_->gdb_flag());
   LogAndSetEnv("ramdisk_image_path", config_->final_ramdisk_path());
   LogAndSetEnv("kernel_cmdline", kernel_cmdline_);
-  LogAndSetEnv("virtual_disk_paths", JoinString(config_->virtual_disk_paths(),
+  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());
diff --git a/host_package.mk b/host_package.mk
index 8c1d134..71c552f 100644
--- a/host_package.mk
+++ b/host_package.mk
@@ -35,7 +35,7 @@
     aarch64-linux-gnu/libepoxy.so.0 \
     aarch64-linux-gnu/libgbm.so.1 \
     aarch64-linux-gnu/libminijail.so \
-    aarch64-linux-gnu/libvirglrenderer.so.0 \
+    aarch64-linux-gnu/libvirglrenderer.so.1 \
     x86_64-linux-gnu/crosvm \
     x86_64-linux-gnu/libepoxy.so.0 \
     x86_64-linux-gnu/libgbm.so.1 \
@@ -51,7 +51,6 @@
     webRTC \
 
 cvd_host_tests := \
-    cuttlefish_thread_test \
     monotonic_time_test \
     cuttlefish_net_tests \
 
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 4706000..3e5fe6b 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -62,6 +62,9 @@
 
 # Make the userdata partition 4.25G to accomodate ASAN and CTS
 BOARD_USERDATAIMAGE_PARTITION_SIZE := 4563402752
+TARGET_USERIMAGES_SPARSE_F2FS_DISABLED := true
+BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE := f2fs
+TARGET_USERIMAGES_USE_F2FS := true
 
 # Cache partition size: 64M
 BOARD_CACHEIMAGE_PARTITION_SIZE := 67108864
@@ -126,10 +129,6 @@
 
 INIT_BOOTCHART := true
 
-# Need this so that the application's loop on reading input can be synchronized
-# with HW VSYNC
-TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK := true
-
 # Settings for dhcpcd-6.8.2.
 DHCPCD_USE_IPV6 := no
 DHCPCD_USE_DBUS := no
diff --git a/shared/config/fstab b/shared/config/fstab
index 1016b62..20685f8 100644
--- a/shared/config/fstab
+++ b/shared/config/fstab
@@ -1,9 +1,9 @@
 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/metadata /metadata ext4 nodev,noatime,nosuid,errors=panic wait,formattable,first_stage_mount
+/dev/block/by-name/userdata /data f2fs nodev,noatime,nosuid 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
 vendor /vendor ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect
diff --git a/shared/config/manifest.xml b/shared/config/manifest.xml
index 5417cde..0343cd2 100644
--- a/shared/config/manifest.xml
+++ b/shared/config/manifest.xml
@@ -85,26 +85,6 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <!-- TODO (b/130078384): -->
-    <hal format="hidl">
-        <name>android.hardware.camera.provider</name>
-        <transport>hwbinder</transport>
-        <impl level="generic"></impl>
-        <version>2.4</version>
-        <interface>
-            <name>ICameraProvider</name>
-            <instance>internal/0</instance>
-        </interface>
-    </hal>
-    <hal format="hidl">
-        <name>android.hardware.configstore</name>
-        <transport>hwbinder</transport>
-        <version>1.1</version>
-        <interface>
-            <name>ISurfaceFlingerConfigs</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
     <!-- TODO (b/130078386):
     <hal format="hidl">
         <name>android.hardware.confirmationui</name>
@@ -128,14 +108,6 @@
     </hal>
     -->
     <hal format="hidl">
-        <name>android.hardware.drm</name>
-        <transport>hwbinder</transport>
-        <fqname>@1.0::ICryptoFactory/default</fqname>
-        <fqname>@1.0::IDrmFactory/default</fqname>
-        <fqname>@1.2::ICryptoFactory/clearkey</fqname>
-        <fqname>@1.2::IDrmFactory/clearkey</fqname>
-    </hal>
-    <hal format="hidl">
         <name>android.hardware.dumpstate</name>
         <transport>hwbinder</transport>
         <version>1.1</version>
diff --git a/shared/config/task_profiles.json b/shared/config/task_profiles.json
index 29bcf30..b883c46 100644
--- a/shared/config/task_profiles.json
+++ b/shared/config/task_profiles.json
@@ -33,12 +33,12 @@
     {
       "Name": "UClampMin",
       "Controller": "cpu",
-      "File": "cpu.util.min"
+      "File": "cpu.uclamp.min"
     },
     {
       "Name": "UClampMax",
       "Controller": "cpu",
-      "File": "cpu.util.max"
+      "File": "cpu.uclamp.max"
     }
   ],
 
diff --git a/shared/device.mk b/shared/device.mk
index 5d4408c..40773b6 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -106,7 +106,22 @@
 
 # GL implementation for virgl
 PRODUCT_PACKAGES += \
-    libGLES_mesa
+    libGLES_mesa \
+
+# GL/Vk implementation for gfxstream
+PRODUCT_PACKAGES += \
+    vulkan.ranchu \
+    hwcomposer.ranchu \
+    libandroidemu \
+    libOpenglCodecCommon \
+    libOpenglSystemCommon \
+    libGLESv1_CM_emulation \
+    lib_renderControl_enc \
+    libEGL_emulation \
+    libGLESv2_enc \
+    libvulkan_enc \
+    libGLESv2_emulation \
+    libGLESv1_enc
 
 #
 # Packages for the Vulkan implementation
@@ -238,8 +253,6 @@
 # Drm HAL
 #
 PRODUCT_PACKAGES += \
-    android.hardware.drm@1.0-impl \
-    android.hardware.drm@1.0-service \
     android.hardware.drm@1.3-service.clearkey \
     android.hardware.drm@1.3-service.widevine
 
@@ -253,9 +266,9 @@
 # Camera
 #
 PRODUCT_PACKAGES += \
-    android.hardware.camera.provider@2.4-service-google \
+    android.hardware.camera.provider@2.6-service-google \
     libgooglecamerahwl_impl \
-    android.hardware.camera.provider@2.4-impl-google \
+    android.hardware.camera.provider@2.6-impl-google \
 
 #
 # Gatekeeper
@@ -382,3 +395,7 @@
 PRODUCT_EXTRA_VNDK_VERSIONS := 28 29
 
 PRODUCT_SOONG_NAMESPACES += external/mesa3d
+
+# Need this so that the application's loop on reading input can be synchronized
+# with HW VSYNC
+PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.surface_flinger.running_without_sync_framework=true
diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts
index 1754d6f..c881257 100644
--- a/shared/sepolicy/vendor/file_contexts
+++ b/shared/sepolicy/vendor/file_contexts
@@ -54,7 +54,7 @@
 /vendor/bin/vport_trigger  u:object_r:vport_trigger_exec:s0
 /vendor/bin/rename_netiface  u:object_r:rename_netiface_exec:s0
 /vendor/bin/hw/libcuttlefish-rild  u:object_r:libcuttlefish_rild_exec:s0
-/vendor/bin/hw/android\.hardware\.camera\.provider@2\.4-service-google u:object_r:hal_camera_default_exec:s0
+/vendor/bin/hw/android\.hardware\.camera\.provider@2\.6-service-google u:object_r:hal_camera_default_exec:s0
 /vendor/bin/hw/android\.hardware\.power\.stats@1\.0-service\.mock  u:object_r:hal_power_stats_default_exec:s0
 /vendor/bin/hw/android\.hardware\.bluetooth@1\.1-service\.sim  u:object_r:hal_bluetooth_sim_exec:s0
 /vendor/bin/hw/android\.hardware\.drm@[0-9]+\.[0-9]+-service\.clearkey  u:object_r:hal_drm_clearkey_exec:s0
@@ -89,3 +89,16 @@
 /vendor/lib(64)?/hw/vulkan.pastel.so  u:object_r:same_process_hal_file:s0
 /vendor/lib(64)?/libcuttlefish_fs.so  u:object_r:same_process_hal_file:s0
 /vendor/lib(64)?/vsoc_lib.so  u:object_r:same_process_hal_file:s0
+
+# gfxstream (to be better factored (fewer libraries?))
+/vendor/lib(64)?/hw/vulkan\.ranchu\.so   u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libEGL_emulation\.so          u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libGLESv1_CM_emulation\.so    u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libGLESv2_emulation\.so       u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libOpenglCodecCommon\.so       u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libOpenglSystemCommon\.so       u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/lib_renderControl_enc\.so       u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libGLESv1_enc\.so       u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libGLESv2_enc\.so       u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libvulkan_enc\.so       u:object_r:same_process_hal_file:s0
+/vendor/lib(64)?/libandroidemu\.so       u:object_r:same_process_hal_file:s0
diff --git a/shared/sepolicy/vendor/genfs_contexts b/shared/sepolicy/vendor/genfs_contexts
index 76d034d..36a5a07 100644
--- a/shared/sepolicy/vendor/genfs_contexts
+++ b/shared/sepolicy/vendor/genfs_contexts
@@ -4,9 +4,6 @@
 genfscon sysfs /devices/platform/rtc-test.0/rtc/rtc0/hctosys u:object_r:sysfs_rtc:s0
 genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/hctosys u:object_r:sysfs_rtc:s0
 genfscon sysfs /devices/platform/rtc-test.2/rtc/rtc2/hctosys u:object_r:sysfs_rtc:s0
-genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/wakeup1  u:object_r:sysfs_wakeup:s0
-genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup0  u:object_r:sysfs_wakeup:s0
-genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
 genfscon sysfs /devices/pci0000:00/0000:00:04.0/virtio2/net u:object_r:sysfs_net:s0 # qemu buried_eth0 & wlan0
 genfscon sysfs /devices/pci0000:00/0000:00:05.0/virtio3/net u:object_r:sysfs_net:s0 # qemu rmnet0
 genfscon sysfs /devices/pci0000:00/0000:00:07.0/virtio6/net u:object_r:sysfs_net:s0 # crosvm buried_eth0 & wlan0
@@ -16,3 +13,12 @@
 genfscon sysfs /devices/pci0000:00/0000:00:0a.0/subsystem_vendor u:object_r:sysfs_gpu:s0
 genfscon sysfs /devices/pci0000:00/0000:00:0a.0/uevent u:object_r:sysfs_gpu:s0
 genfscon sysfs /devices/pci0000:00/0000:00:0a.0/vendor u:object_r:sysfs_gpu:s0
+
+# TODO(b/148802006): Work around core policy sysfs_wakeup label not working
+# All kernels
+genfscon sysfs /devices/platform/rtc-test.1/wakeup/wakeup0  u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup2 u:object_r:sysfs_wakeup:s0
+# Kernels <=5.5
+genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/wakeup1  u:object_r:sysfs_wakeup:s0
+# Kernels >5.5
+genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc1/alarmtimer.0.auto/wakeup/wakeup1  u:object_r:sysfs_wakeup:s0
diff --git a/shared/sepolicy/vendor/hal_graphics_composer_default.te b/shared/sepolicy/vendor/hal_graphics_composer_default.te
index 6b89710..c3a7cab 100644
--- a/shared/sepolicy/vendor/hal_graphics_composer_default.te
+++ b/shared/sepolicy/vendor/hal_graphics_composer_default.te
@@ -1,3 +1,4 @@
+hal_client_domain(hal_graphics_composer_default, hal_graphics_allocator);
 vndbinder_use(hal_graphics_composer_default)
 
 allow hal_graphics_composer_default self:netlink_kobject_uevent_socket { bind create read };
diff --git a/tests/hidl/Android.bp b/tests/hal/Android.bp
similarity index 70%
rename from tests/hidl/Android.bp
rename to tests/hal/Android.bp
index a1827e3..f8a3d83 100644
--- a/tests/hidl/Android.bp
+++ b/tests/hal/Android.bp
@@ -1,6 +1,6 @@
 cc_test {
-    name: "hidl_implementation_test",
-    srcs: ["hidl_implementation_test.cpp"],
+    name: "hal_implementation_test",
+    srcs: ["hal_implementation_test.cpp"],
     static_libs: [
         "libhidlmetadata",
         "libhidl-gen-utils",
diff --git a/tests/hidl/OWNERS b/tests/hal/OWNERS
similarity index 100%
rename from tests/hidl/OWNERS
rename to tests/hal/OWNERS
diff --git a/tests/hidl/hidl_implementation_test.cpp b/tests/hal/hal_implementation_test.cpp
similarity index 98%
rename from tests/hidl/hidl_implementation_test.cpp
rename to tests/hal/hal_implementation_test.cpp
index 6661215..6fa730d 100644
--- a/tests/hidl/hidl_implementation_test.cpp
+++ b/tests/hal/hal_implementation_test.cpp
@@ -44,8 +44,6 @@
     "android.hardware.bluetooth.a2dp@1.0",
     "android.hardware.broadcastradio@1.1",
     "android.hardware.broadcastradio@2.0",
-    "android.hardware.camera.provider@2.5",
-    "android.hardware.camera.provider@2.6",
     "android.hardware.cas.native@1.0",
     "android.hardware.confirmationui@1.0",
     "android.hardware.contexthub@1.0",