Merge "Move Cuttlefish with QEMU onto Ranchu hwcomposer"
diff --git a/build/Android.bp b/build/Android.bp
index 0c3bb5f..2a28d4d 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -51,6 +51,7 @@
     "kernel_log_monitor",
     "launch_cvd",
     "libgrpc++",
+    "libgrpc++_unsecure",
     "log_tee",
     "logcat_receiver",
     "lpmake",
@@ -78,6 +79,11 @@
     "webrtc_operator",
 ]
 
+cvd_bluetooth_config_files = [
+    "controller_properties.json",
+    "default_commands",
+]
+
 cvd_host_tests = [
     "cuttlefish_net_tests",
     "modem_simulator_test",
@@ -152,7 +158,8 @@
     multilib: {
         common: {
             deps: cvd_host_webrtc_assets +
-                cvd_host_model_simulator_files,
+                cvd_host_model_simulator_files +
+                cvd_bluetooth_config_files,
         },
     },
 
diff --git a/build/cvd-host-package.go b/build/cvd-host-package.go
index d000cf0..b025386 100644
--- a/build/cvd-host-package.go
+++ b/build/cvd-host-package.go
@@ -42,7 +42,8 @@
 
 type dependencyTag struct {
 	blueprint.BaseDependencyTag
-	android.InstallAlwaysNeededDependencyTag // to force installation of the deps
+	android.InstallAlwaysNeededDependencyTag // to force installation of both "deps" and manually added dependencies
+	android.PackagingItemAlwaysDepTag  // to force packaging of both "deps" and manually added dependencies
 }
 
 var cvdHostPackageDependencyTag = dependencyTag{}
diff --git a/common/libs/fs/shared_fd.cpp b/common/libs/fs/shared_fd.cpp
index f5c12c6..8b21f0c 100644
--- a/common/libs/fs/shared_fd.cpp
+++ b/common/libs/fs/shared_fd.cpp
@@ -319,6 +319,15 @@
   }
 }
 
+SharedFD SharedFD::Mkstemp(std::string* path) {
+  int fd = mkstemp(path->data());
+  if (fd == -1) {
+    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, errno)));
+  } else {
+    return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(fd, 0)));
+  }
+}
+
 SharedFD SharedFD::ErrorFD(int error) {
   return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(-1, error)));
 }
diff --git a/common/libs/fs/shared_fd.h b/common/libs/fs/shared_fd.h
index ad962de..e712ab9 100644
--- a/common/libs/fs/shared_fd.h
+++ b/common/libs/fs/shared_fd.h
@@ -129,6 +129,7 @@
   static bool Pipe(SharedFD* fd0, SharedFD* fd1);
   static SharedFD Event(int initval = 0, int flags = 0);
   static SharedFD MemfdCreate(const std::string& name, unsigned int flags = 0);
+  static SharedFD Mkstemp(std::string* path);
   static bool SocketPair(int domain, int type, int protocol, SharedFD* fd0,
                          SharedFD* fd1);
   static SharedFD Socket(int domain, int socket_type, int protocol);
diff --git a/common/libs/utils/environment.cpp b/common/libs/utils/environment.cpp
index 5c0de51..ec27290 100644
--- a/common/libs/utils/environment.cpp
+++ b/common/libs/utils/environment.cpp
@@ -21,6 +21,8 @@
 #include <stdio.h>
 #include <iostream>
 
+#include <android-base/logging.h>
+
 namespace cuttlefish {
 
 std::string StringFromEnv(const std::string& varname,
@@ -39,7 +41,7 @@
  *
  * @return arch string on success, "" on failure
  */
-std::string HostArch() {
+std::string HostArchStr() {
   static std::string arch;
   static bool cached = false;
 
@@ -83,6 +85,29 @@
   return arch;
 }
 
+Arch HostArch() {
+  std::string arch_str = HostArchStr();
+  if (arch_str == "aarch64") {
+    return Arch::Arm64;
+  } else if (arch_str == "arm") {
+    return Arch::Arm;
+  } else if (arch_str == "x86_64") {
+    return Arch::X86_64;
+  } else if (arch_str.size() == 4 && arch_str[0] == 'i' && arch_str[2] == '8' &&
+             arch_str[3] == '6') {
+    return Arch::X86;
+  } else {
+    LOG(FATAL) << "Unknown host architecture: " << arch_str;
+    return Arch::X86;
+  }
+}
+
+bool IsHostCompatible(Arch arch) {
+  Arch host_arch = HostArch();
+  return arch == host_arch || (arch == Arch::Arm && host_arch == Arch::Arm64) ||
+         (arch == Arch::X86 && host_arch == Arch::X86_64);
+}
+
 static bool IsRunningInDocker() {
   // if /.dockerenv exists, it's inside a docker container
   static std::string docker_env_path("/.dockerenv");
diff --git a/common/libs/utils/environment.h b/common/libs/utils/environment.h
index 60bcbb6..004a849 100644
--- a/common/libs/utils/environment.h
+++ b/common/libs/utils/environment.h
@@ -19,10 +19,19 @@
 
 namespace cuttlefish {
 
+enum class Arch {
+  Arm,
+  Arm64,
+  X86,
+  X86_64,
+};
+
 std::string StringFromEnv(const std::string& varname,
                           const std::string& defval);
 
-std::string HostArch();
+std::string HostArchStr();
+Arch HostArch();
+bool IsHostCompatible(Arch arch);
 
 bool IsRunningInContainer();
 
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
index df91c23..d1f5f59 100644
--- a/common/libs/utils/network.cpp
+++ b/common/libs/utils/network.cpp
@@ -96,7 +96,7 @@
     return tap_fd;
   }
 
-  if (HostArch() == "aarch64") {
+  if (HostArch() == Arch::Arm64) {
     auto tapsetiff_path = DefaultHostArtifactsPath("bin/tapsetiff");
     Command cmd(tapsetiff_path);
     cmd.AddParameter(tap_fd);
diff --git a/guest/hals/bt/data/Android.bp b/guest/hals/bt/data/Android.bp
new file mode 100644
index 0000000..845223f
--- /dev/null
+++ b/guest/hals/bt/data/Android.bp
@@ -0,0 +1,9 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+prebuilt_etc_host {
+    name: "default_commands",
+    src: "default_commands",
+    sub_dir: "rootcanal/data",
+}
diff --git a/guest/hals/bt/data/default_commands b/guest/hals/bt/data/default_commands
new file mode 100644
index 0000000..9182915
--- /dev/null
+++ b/guest/hals/bt/data/default_commands
@@ -0,0 +1,4 @@
+add beacon be:ac:01:55:00:01 1000
+add_device_to_phy 0 1
+add beacon be:ac:01:55:00:02 1000
+add_device_to_phy 1 1
\ No newline at end of file
diff --git a/guest/hals/keymint/OWNERS b/guest/hals/keymint/OWNERS
new file mode 100644
index 0000000..fb015cb
--- /dev/null
+++ b/guest/hals/keymint/OWNERS
@@ -0,0 +1 @@
+include platform/system/keymaster:/OWNERS
diff --git a/guest/hals/keymint/remote/Android.bp b/guest/hals/keymint/remote/Android.bp
new file mode 100644
index 0000000..bb78ff2
--- /dev/null
+++ b/guest/hals/keymint/remote/Android.bp
@@ -0,0 +1,60 @@
+//
+// 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "android.hardware.security.keymint-service.remote",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.security.keymint-service.remote.rc"],
+    vintf_fragments: [
+        "android.hardware.security.keymint-service.remote.xml",
+        "android.hardware.security.sharedsecret-service.remote.xml",
+        "android.hardware.security.secureclock-service.remote.xml",
+    ],
+    vendor: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+        "libcppbor_external",
+        "libcrypto",
+        "libcuttlefish_fs",
+        "libcuttlefish_security",
+        "libhardware",
+        "libkeymint",
+        "libkeymaster_messages",
+        "liblog",
+        "libutils",
+    ],
+    srcs: [
+        "remote_keymint_device.cpp",
+        "remote_keymint_operation.cpp",
+        "remote_keymaster.cpp",
+        "remote_secure_clock.cpp",
+        "service.cpp",
+    ],
+    defaults: [
+        "cuttlefish_guest_only",
+    ],
+}
diff --git a/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.rc b/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.rc
new file mode 100644
index 0000000..422fe17
--- /dev/null
+++ b/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.rc
@@ -0,0 +1,3 @@
+service vendor.keymint-remote /vendor/bin/hw/android.hardware.security.keymint-service.remote
+    class early_hal
+    user nobody
diff --git a/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml b/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml
new file mode 100644
index 0000000..4aa05ef
--- /dev/null
+++ b/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <fqname>IKeyMintDevice/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <fqname>IRemotelyProvisionedComponent/default</fqname>
+    </hal>
+</manifest>
diff --git a/guest/hals/keymint/remote/android.hardware.security.secureclock-service.remote.xml b/guest/hals/keymint/remote/android.hardware.security.secureclock-service.remote.xml
new file mode 100644
index 0000000..c0ff775
--- /dev/null
+++ b/guest/hals/keymint/remote/android.hardware.security.secureclock-service.remote.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.secureclock</name>
+        <fqname>ISecureClock/default</fqname>
+    </hal>
+</manifest>
diff --git a/guest/hals/keymint/remote/android.hardware.security.sharedsecret-service.remote.xml b/guest/hals/keymint/remote/android.hardware.security.sharedsecret-service.remote.xml
new file mode 100644
index 0000000..d37981f
--- /dev/null
+++ b/guest/hals/keymint/remote/android.hardware.security.sharedsecret-service.remote.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.sharedsecret</name>
+        <fqname>ISharedSecret/default</fqname>
+    </hal>
+</manifest>
diff --git a/guest/hals/keymint/remote/remote_keymaster.cpp b/guest/hals/keymint/remote/remote_keymaster.cpp
new file mode 100644
index 0000000..4a37cf0
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_keymaster.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright 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.
+ */
+
+#include "remote_keymaster.h"
+
+#include <android-base/logging.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/keymaster_configuration.h>
+
+namespace keymaster {
+
+RemoteKeymaster::RemoteKeymaster(cuttlefish::KeymasterChannel* channel)
+    : channel_(channel) {}
+
+RemoteKeymaster::~RemoteKeymaster() {}
+
+void RemoteKeymaster::ForwardCommand(AndroidKeymasterCommand command,
+                                     const Serializable& req,
+                                     KeymasterResponse* rsp) {
+  if (!channel_->SendRequest(command, req)) {
+    LOG(ERROR) << "Failed to send keymaster message: " << command;
+    rsp->error = KM_ERROR_UNKNOWN_ERROR;
+    return;
+  }
+  auto response = channel_->ReceiveMessage();
+  if (!response) {
+    LOG(ERROR) << "Failed to receive keymaster response: " << command;
+    rsp->error = KM_ERROR_UNKNOWN_ERROR;
+    return;
+  }
+  const uint8_t* buffer = response->payload;
+  const uint8_t* buffer_end = response->payload + response->payload_size;
+  if (!rsp->Deserialize(&buffer, buffer_end)) {
+    LOG(ERROR) << "Failed to deserialize keymaster response: " << command;
+    rsp->error = KM_ERROR_UNKNOWN_ERROR;
+    return;
+  }
+}
+
+bool RemoteKeymaster::Initialize() {
+  // We don't need to bother with GetVersion, because CF HAL and remote sides
+  // are always compiled together, so will never disagree about message
+  // versions.
+  ConfigureRequest req(message_version());
+  req.os_version = GetOsVersion();
+  req.os_patchlevel = GetOsPatchlevel();
+
+  ConfigureResponse rsp(message_version());
+  Configure(req, &rsp);
+
+  if (rsp.error != KM_ERROR_OK) {
+    LOG(ERROR) << "Failed to configure keymaster: " << rsp.error;
+    return false;
+  }
+
+  return true;
+}
+
+void RemoteKeymaster::GetVersion(const GetVersionRequest& request,
+                                 GetVersionResponse* response) {
+  ForwardCommand(GET_VERSION, request, response);
+}
+
+void RemoteKeymaster::SupportedAlgorithms(
+    const SupportedAlgorithmsRequest& request,
+    SupportedAlgorithmsResponse* response) {
+  ForwardCommand(GET_SUPPORTED_ALGORITHMS, request, response);
+}
+
+void RemoteKeymaster::SupportedBlockModes(
+    const SupportedBlockModesRequest& request,
+    SupportedBlockModesResponse* response) {
+  ForwardCommand(GET_SUPPORTED_BLOCK_MODES, request, response);
+}
+
+void RemoteKeymaster::SupportedPaddingModes(
+    const SupportedPaddingModesRequest& request,
+    SupportedPaddingModesResponse* response) {
+  ForwardCommand(GET_SUPPORTED_PADDING_MODES, request, response);
+}
+
+void RemoteKeymaster::SupportedDigests(const SupportedDigestsRequest& request,
+                                       SupportedDigestsResponse* response) {
+  ForwardCommand(GET_SUPPORTED_DIGESTS, request, response);
+}
+
+void RemoteKeymaster::SupportedImportFormats(
+    const SupportedImportFormatsRequest& request,
+    SupportedImportFormatsResponse* response) {
+  ForwardCommand(GET_SUPPORTED_IMPORT_FORMATS, request, response);
+}
+
+void RemoteKeymaster::SupportedExportFormats(
+    const SupportedExportFormatsRequest& request,
+    SupportedExportFormatsResponse* response) {
+  ForwardCommand(GET_SUPPORTED_EXPORT_FORMATS, request, response);
+}
+
+void RemoteKeymaster::AddRngEntropy(const AddEntropyRequest& request,
+                                    AddEntropyResponse* response) {
+  ForwardCommand(ADD_RNG_ENTROPY, request, response);
+}
+
+void RemoteKeymaster::Configure(const ConfigureRequest& request,
+                                ConfigureResponse* response) {
+  ForwardCommand(CONFIGURE, request, response);
+}
+
+void RemoteKeymaster::GenerateKey(const GenerateKeyRequest& request,
+                                  GenerateKeyResponse* response) {
+  GenerateKeyRequest datedRequest(request.message_version);
+  datedRequest.key_description = request.key_description;
+
+  if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
+    datedRequest.key_description.push_back(TAG_CREATION_DATETIME,
+                                           java_time(time(NULL)));
+  }
+
+  ForwardCommand(GENERATE_KEY, datedRequest, response);
+}
+
+void RemoteKeymaster::GetKeyCharacteristics(
+    const GetKeyCharacteristicsRequest& request,
+    GetKeyCharacteristicsResponse* response) {
+  ForwardCommand(GET_KEY_CHARACTERISTICS, request, response);
+}
+
+void RemoteKeymaster::ImportKey(const ImportKeyRequest& request,
+                                ImportKeyResponse* response) {
+  ForwardCommand(IMPORT_KEY, request, response);
+}
+
+void RemoteKeymaster::ImportWrappedKey(const ImportWrappedKeyRequest& request,
+                                       ImportWrappedKeyResponse* response) {
+  ForwardCommand(IMPORT_WRAPPED_KEY, request, response);
+}
+
+void RemoteKeymaster::ExportKey(const ExportKeyRequest& request,
+                                ExportKeyResponse* response) {
+  ForwardCommand(EXPORT_KEY, request, response);
+}
+
+void RemoteKeymaster::AttestKey(const AttestKeyRequest& request,
+                                AttestKeyResponse* response) {
+  ForwardCommand(ATTEST_KEY, request, response);
+}
+
+void RemoteKeymaster::UpgradeKey(const UpgradeKeyRequest& request,
+                                 UpgradeKeyResponse* response) {
+  ForwardCommand(UPGRADE_KEY, request, response);
+}
+
+void RemoteKeymaster::DeleteKey(const DeleteKeyRequest& request,
+                                DeleteKeyResponse* response) {
+  ForwardCommand(DELETE_KEY, request, response);
+}
+
+void RemoteKeymaster::DeleteAllKeys(const DeleteAllKeysRequest& request,
+                                    DeleteAllKeysResponse* response) {
+  ForwardCommand(DELETE_ALL_KEYS, request, response);
+}
+
+void RemoteKeymaster::BeginOperation(const BeginOperationRequest& request,
+                                     BeginOperationResponse* response) {
+  ForwardCommand(BEGIN_OPERATION, request, response);
+}
+
+void RemoteKeymaster::UpdateOperation(const UpdateOperationRequest& request,
+                                      UpdateOperationResponse* response) {
+  ForwardCommand(UPDATE_OPERATION, request, response);
+}
+
+void RemoteKeymaster::FinishOperation(const FinishOperationRequest& request,
+                                      FinishOperationResponse* response) {
+  ForwardCommand(FINISH_OPERATION, request, response);
+}
+
+void RemoteKeymaster::AbortOperation(const AbortOperationRequest& request,
+                                     AbortOperationResponse* response) {
+  ForwardCommand(ABORT_OPERATION, request, response);
+}
+
+GetHmacSharingParametersResponse RemoteKeymaster::GetHmacSharingParameters() {
+  // Unused empty buffer to allow ForwardCommand to have something to serialize
+  Buffer request;
+  GetHmacSharingParametersResponse response(message_version());
+  ForwardCommand(GET_HMAC_SHARING_PARAMETERS, request, &response);
+  return response;
+}
+
+ComputeSharedHmacResponse RemoteKeymaster::ComputeSharedHmac(
+    const ComputeSharedHmacRequest& request) {
+  ComputeSharedHmacResponse response(message_version());
+  ForwardCommand(COMPUTE_SHARED_HMAC, request, &response);
+  return response;
+}
+
+VerifyAuthorizationResponse RemoteKeymaster::VerifyAuthorization(
+    const VerifyAuthorizationRequest& request) {
+  VerifyAuthorizationResponse response(message_version());
+  ForwardCommand(VERIFY_AUTHORIZATION, request, &response);
+  return response;
+}
+
+DeviceLockedResponse RemoteKeymaster::DeviceLocked(
+    const DeviceLockedRequest& request) {
+  DeviceLockedResponse response(message_version());
+  ForwardCommand(DEVICE_LOCKED, request, &response);
+  return response;
+}
+
+EarlyBootEndedResponse RemoteKeymaster::EarlyBootEnded() {
+  // Unused empty buffer to allow ForwardCommand to have something to serialize
+  Buffer request;
+  EarlyBootEndedResponse response(message_version());
+  ForwardCommand(EARLY_BOOT_ENDED, request, &response);
+  return response;
+}
+
+void RemoteKeymaster::GenerateTimestampToken(GenerateTimestampTokenRequest&,
+                                             GenerateTimestampTokenResponse*) {
+  // TODO(aosp/1641315): Send a message to the host.
+  // ForwardCommand(GENERATE_TIMESTAMP_TOKEN, request, response);
+}
+
+}  // namespace keymaster
diff --git a/guest/hals/keymint/remote/remote_keymaster.h b/guest/hals/keymint/remote/remote_keymaster.h
new file mode 100644
index 0000000..6cc6c7b
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_keymaster.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef REMOTE_KEYMASTER_H_
+#define REMOTE_KEYMASTER_H_
+
+#include <keymaster/android_keymaster_messages.h>
+
+#include "common/libs/security/keymaster_channel.h"
+
+namespace keymaster {
+
+class RemoteKeymaster {
+ private:
+  cuttlefish::KeymasterChannel* channel_;
+
+  void ForwardCommand(AndroidKeymasterCommand command, const Serializable& req,
+                      KeymasterResponse* rsp);
+
+ public:
+  RemoteKeymaster(cuttlefish::KeymasterChannel*);
+  ~RemoteKeymaster();
+  bool Initialize();
+  void GetVersion(const GetVersionRequest& request,
+                  GetVersionResponse* response);
+  void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
+                           SupportedAlgorithmsResponse* response);
+  void SupportedBlockModes(const SupportedBlockModesRequest& request,
+                           SupportedBlockModesResponse* response);
+  void SupportedPaddingModes(const SupportedPaddingModesRequest& request,
+                             SupportedPaddingModesResponse* response);
+  void SupportedDigests(const SupportedDigestsRequest& request,
+                        SupportedDigestsResponse* response);
+  void SupportedImportFormats(const SupportedImportFormatsRequest& request,
+                              SupportedImportFormatsResponse* response);
+  void SupportedExportFormats(const SupportedExportFormatsRequest& request,
+                              SupportedExportFormatsResponse* response);
+  void AddRngEntropy(const AddEntropyRequest& request,
+                     AddEntropyResponse* response);
+  void Configure(const ConfigureRequest& request, ConfigureResponse* response);
+  void GenerateKey(const GenerateKeyRequest& request,
+                   GenerateKeyResponse* response);
+  void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
+                             GetKeyCharacteristicsResponse* response);
+  void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
+  void ImportWrappedKey(const ImportWrappedKeyRequest& request,
+                        ImportWrappedKeyResponse* response);
+  void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response);
+  void AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response);
+  void UpgradeKey(const UpgradeKeyRequest& request,
+                  UpgradeKeyResponse* response);
+  void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);
+  void DeleteAllKeys(const DeleteAllKeysRequest& request,
+                     DeleteAllKeysResponse* response);
+  void BeginOperation(const BeginOperationRequest& request,
+                      BeginOperationResponse* response);
+  void UpdateOperation(const UpdateOperationRequest& request,
+                       UpdateOperationResponse* response);
+  void FinishOperation(const FinishOperationRequest& request,
+                       FinishOperationResponse* response);
+  void AbortOperation(const AbortOperationRequest& request,
+                      AbortOperationResponse* response);
+  GetHmacSharingParametersResponse GetHmacSharingParameters();
+  ComputeSharedHmacResponse ComputeSharedHmac(
+      const ComputeSharedHmacRequest& request);
+  VerifyAuthorizationResponse VerifyAuthorization(
+      const VerifyAuthorizationRequest& request);
+  DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
+  EarlyBootEndedResponse EarlyBootEnded();
+  void GenerateTimestampToken(GenerateTimestampTokenRequest& request,
+                              GenerateTimestampTokenResponse* response);
+
+  // CF HAL and remote sides are always compiled together, so will never
+  // disagree about message versions.
+  uint32_t message_version() { return kDefaultMessageVersion; }
+};
+
+}  // namespace keymaster
+
+#endif  // REMOTE_KEYMASTER_H_
diff --git a/guest/hals/keymint/remote/remote_keymint_device.cpp b/guest/hals/keymint/remote/remote_keymint_device.cpp
new file mode 100644
index 0000000..c9171c8
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_keymint_device.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "android.hardware.security.keymint-impl"
+#include <android-base/logging.h>
+
+#include "guest/hals/keymint/remote/remote_keymint_device.h"
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/contexts/pure_soft_keymaster_context.h>
+#include <keymaster/keymaster_configuration.h>
+
+#include "KeyMintUtils.h"
+#include "guest/hals/keymint/remote/remote_keymint_operation.h"
+
+namespace aidl::android::hardware::security::keymint {
+
+using namespace ::keymaster;
+using namespace km_utils;
+using secureclock::TimeStampToken;
+
+namespace {
+
+vector<KeyCharacteristics> convertKeyCharacteristics(
+    SecurityLevel keyMintSecurityLevel, const AuthorizationSet& sw_enforced,
+    const AuthorizationSet& hw_enforced) {
+  KeyCharacteristics keyMintEnforced{keyMintSecurityLevel, {}};
+
+  if (keyMintSecurityLevel != SecurityLevel::SOFTWARE) {
+    // We're pretending to be TRUSTED_ENVIRONMENT or STRONGBOX.
+    keyMintEnforced.authorizations = kmParamSet2Aidl(hw_enforced);
+    // Put all the software authorizations in the keystore list.
+    KeyCharacteristics keystoreEnforced{SecurityLevel::KEYSTORE,
+                                        kmParamSet2Aidl(sw_enforced)};
+    return {std::move(keyMintEnforced), std::move(keystoreEnforced)};
+  }
+
+  KeyCharacteristics keystoreEnforced{SecurityLevel::KEYSTORE, {}};
+  CHECK(hw_enforced.empty())
+      << "Hardware-enforced list is non-empty for pure SW KeyMint";
+
+  // This is a pure software implementation, so all tags are in sw_enforced.
+  // We need to walk through the SW-enforced list and figure out which tags to
+  // return in the software list and which in the keystore list.
+
+  for (auto& entry : sw_enforced) {
+    switch (entry.tag) {
+      /* Invalid and unused */
+      case KM_TAG_ECIES_SINGLE_HASH_MODE:
+      case KM_TAG_INVALID:
+      case KM_TAG_KDF:
+      case KM_TAG_ROLLBACK_RESISTANCE:
+        CHECK(false) << "We shouldn't see tag " << entry.tag;
+        break;
+
+      /* Unimplemented */
+      case KM_TAG_ALLOW_WHILE_ON_BODY:
+      case KM_TAG_BOOTLOADER_ONLY:
+      case KM_TAG_EARLY_BOOT_ONLY:
+      case KM_TAG_ROLLBACK_RESISTANT:
+      case KM_TAG_STORAGE_KEY:
+      case KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
+      case KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
+        break;
+
+      /* Unenforceable */
+      case KM_TAG_CREATION_DATETIME:
+        break;
+
+      /* Disallowed in KeyCharacteristics */
+      case KM_TAG_APPLICATION_DATA:
+      case KM_TAG_ATTESTATION_APPLICATION_ID:
+        break;
+
+      /* Not key characteristics */
+      case KM_TAG_ASSOCIATED_DATA:
+      case KM_TAG_ATTESTATION_CHALLENGE:
+      case KM_TAG_ATTESTATION_ID_BRAND:
+      case KM_TAG_ATTESTATION_ID_DEVICE:
+      case KM_TAG_ATTESTATION_ID_IMEI:
+      case KM_TAG_ATTESTATION_ID_MANUFACTURER:
+      case KM_TAG_ATTESTATION_ID_MEID:
+      case KM_TAG_ATTESTATION_ID_MODEL:
+      case KM_TAG_ATTESTATION_ID_PRODUCT:
+      case KM_TAG_ATTESTATION_ID_SERIAL:
+      case KM_TAG_AUTH_TOKEN:
+      case KM_TAG_CERTIFICATE_SERIAL:
+      case KM_TAG_CERTIFICATE_SUBJECT:
+      case KM_TAG_CERTIFICATE_NOT_AFTER:
+      case KM_TAG_CERTIFICATE_NOT_BEFORE:
+      case KM_TAG_CONFIRMATION_TOKEN:
+      case KM_TAG_DEVICE_UNIQUE_ATTESTATION:
+      case KM_TAG_IDENTITY_CREDENTIAL_KEY:
+      case KM_TAG_MAC_LENGTH:
+      case KM_TAG_NONCE:
+      case KM_TAG_RESET_SINCE_ID_ROTATION:
+      case KM_TAG_ROOT_OF_TRUST:
+      case KM_TAG_UNIQUE_ID:
+        break;
+
+      /* KeyMint-enforced */
+      case KM_TAG_ALGORITHM:
+      case KM_TAG_APPLICATION_ID:
+      case KM_TAG_AUTH_TIMEOUT:
+      case KM_TAG_BLOB_USAGE_REQUIREMENTS:
+      case KM_TAG_BLOCK_MODE:
+      case KM_TAG_BOOT_PATCHLEVEL:
+      case KM_TAG_CALLER_NONCE:
+      case KM_TAG_DIGEST:
+      case KM_TAG_EC_CURVE:
+      case KM_TAG_EXPORTABLE:
+      case KM_TAG_INCLUDE_UNIQUE_ID:
+      case KM_TAG_KEY_SIZE:
+      case KM_TAG_MAX_USES_PER_BOOT:
+      case KM_TAG_MIN_MAC_LENGTH:
+      case KM_TAG_MIN_SECONDS_BETWEEN_OPS:
+      case KM_TAG_NO_AUTH_REQUIRED:
+      case KM_TAG_ORIGIN:
+      case KM_TAG_OS_PATCHLEVEL:
+      case KM_TAG_OS_VERSION:
+      case KM_TAG_PADDING:
+      case KM_TAG_PURPOSE:
+      case KM_TAG_RSA_OAEP_MGF_DIGEST:
+      case KM_TAG_RSA_PUBLIC_EXPONENT:
+      case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
+      case KM_TAG_USER_AUTH_TYPE:
+      case KM_TAG_USER_SECURE_ID:
+      case KM_TAG_VENDOR_PATCHLEVEL:
+        keyMintEnforced.authorizations.push_back(kmParam2Aidl(entry));
+        break;
+
+      /* Keystore-enforced */
+      case KM_TAG_ACTIVE_DATETIME:
+      case KM_TAG_ALL_APPLICATIONS:
+      case KM_TAG_ALL_USERS:
+      case KM_TAG_MAX_BOOT_LEVEL:
+      case KM_TAG_ORIGINATION_EXPIRE_DATETIME:
+      case KM_TAG_USAGE_EXPIRE_DATETIME:
+      case KM_TAG_USER_ID:
+      case KM_TAG_USAGE_COUNT_LIMIT:
+        keystoreEnforced.authorizations.push_back(kmParam2Aidl(entry));
+        break;
+    }
+  }
+
+  vector<KeyCharacteristics> retval;
+  retval.reserve(2);
+  if (!keyMintEnforced.authorizations.empty())
+    retval.push_back(std::move(keyMintEnforced));
+  if (!keystoreEnforced.authorizations.empty())
+    retval.push_back(std::move(keystoreEnforced));
+
+  return retval;
+}
+
+Certificate convertCertificate(const keymaster_blob_t& cert) {
+  return {std::vector<uint8_t>(cert.data, cert.data + cert.data_length)};
+}
+
+vector<Certificate> convertCertificateChain(const CertificateChain& chain) {
+  vector<Certificate> retval;
+  retval.reserve(chain.entry_count);
+  std::transform(chain.begin(), chain.end(), std::back_inserter(retval),
+                 convertCertificate);
+  return retval;
+}
+
+}  // namespace
+
+RemoteKeyMintDevice::RemoteKeyMintDevice(::keymaster::RemoteKeymaster& impl,
+                                         SecurityLevel securityLevel)
+    : impl_(impl), securityLevel_(securityLevel) {}
+
+RemoteKeyMintDevice::~RemoteKeyMintDevice() {}
+
+ScopedAStatus RemoteKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
+  info->versionNumber = 1;
+  info->securityLevel = securityLevel_;
+  info->keyMintName = "RemoteKeyMintDevice";
+  info->keyMintAuthorName = "Google";
+  info->timestampTokenRequired = false;
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
+  if (data.size() == 0) {
+    return ScopedAStatus::ok();
+  }
+
+  AddEntropyRequest request(impl_.message_version());
+  request.random_data.Reinitialize(data.data(), data.size());
+
+  AddEntropyResponse response(impl_.message_version());
+  impl_.AddRngEntropy(request, &response);
+
+  return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus RemoteKeyMintDevice::generateKey(
+    const vector<KeyParameter>& keyParams,
+    const optional<AttestationKey>& attestationKey,
+    KeyCreationResult* creationResult) {
+  GenerateKeyRequest request(impl_.message_version());
+  request.key_description.Reinitialize(KmParamSet(keyParams));
+  if (attestationKey) {
+    request.attestation_signing_key_blob = KeymasterKeyBlob(
+        attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
+    request.attest_key_params.Reinitialize(
+        KmParamSet(attestationKey->attestKeyParams));
+    request.issuer_subject =
+        KeymasterBlob(attestationKey->issuerSubjectName.data(),
+                      attestationKey->issuerSubjectName.size());
+  }
+
+  GenerateKeyResponse response(impl_.message_version());
+  impl_.GenerateKey(request, &response);
+
+  if (response.error != KM_ERROR_OK) {
+    // Note a key difference between this current aidl and previous hal, is
+    // that hal returns void where as aidl returns the error status.  If
+    // aidl returns error, then aidl will not return any change you may make
+    // to the out parameters.  This is quite different from hal where all
+    // output variable can be modified due to hal returning void.
+    //
+    // So the caller need to be aware not to expect aidl functions to clear
+    // the output variables for you in case of error.  If you left some
+    // wrong data set in the out parameters, they will stay there.
+    return kmError2ScopedAStatus(response.error);
+  }
+
+  creationResult->keyBlob = kmBlob2vector(response.key_blob);
+  creationResult->keyCharacteristics = convertKeyCharacteristics(
+      securityLevel_, response.unenforced, response.enforced);
+  creationResult->certificateChain =
+      convertCertificateChain(response.certificate_chain);
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintDevice::importKey(
+    const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+    const vector<uint8_t>& keyData,
+    const optional<AttestationKey>& attestationKey,
+    KeyCreationResult* creationResult) {
+  ImportKeyRequest request(impl_.message_version());
+  request.key_description.Reinitialize(KmParamSet(keyParams));
+  request.key_format = legacy_enum_conversion(keyFormat);
+  request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
+  if (attestationKey) {
+    request.attestation_signing_key_blob = KeymasterKeyBlob(
+        attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
+    request.attest_key_params.Reinitialize(
+        KmParamSet(attestationKey->attestKeyParams));
+    request.issuer_subject =
+        KeymasterBlob(attestationKey->issuerSubjectName.data(),
+                      attestationKey->issuerSubjectName.size());
+  }
+
+  ImportKeyResponse response(impl_.message_version());
+  impl_.ImportKey(request, &response);
+
+  if (response.error != KM_ERROR_OK) {
+    return kmError2ScopedAStatus(response.error);
+  }
+
+  creationResult->keyBlob = kmBlob2vector(response.key_blob);
+  creationResult->keyCharacteristics = convertKeyCharacteristics(
+      securityLevel_, response.unenforced, response.enforced);
+  creationResult->certificateChain =
+      convertCertificateChain(response.certificate_chain);
+
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintDevice::importWrappedKey(
+    const vector<uint8_t>& wrappedKeyData,         //
+    const vector<uint8_t>& wrappingKeyBlob,        //
+    const vector<uint8_t>& maskingKey,             //
+    const vector<KeyParameter>& unwrappingParams,  //
+    int64_t passwordSid, int64_t biometricSid,     //
+    KeyCreationResult* creationResult) {
+  ImportWrappedKeyRequest request(impl_.message_version());
+  request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
+  request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
+  request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
+  request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
+  request.password_sid = static_cast<uint64_t>(passwordSid);
+  request.biometric_sid = static_cast<uint64_t>(biometricSid);
+
+  ImportWrappedKeyResponse response(impl_.message_version());
+  impl_.ImportWrappedKey(request, &response);
+
+  if (response.error != KM_ERROR_OK) {
+    return kmError2ScopedAStatus(response.error);
+  }
+
+  creationResult->keyBlob = kmBlob2vector(response.key_blob);
+  creationResult->keyCharacteristics = convertKeyCharacteristics(
+      securityLevel_, response.unenforced, response.enforced);
+  creationResult->certificateChain =
+      convertCertificateChain(response.certificate_chain);
+
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintDevice::upgradeKey(
+    const vector<uint8_t>& keyBlobToUpgrade,
+    const vector<KeyParameter>& upgradeParams, vector<uint8_t>* keyBlob) {
+  UpgradeKeyRequest request(impl_.message_version());
+  request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+  request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+  UpgradeKeyResponse response(impl_.message_version());
+  impl_.UpgradeKey(request, &response);
+
+  if (response.error != KM_ERROR_OK) {
+    return kmError2ScopedAStatus(response.error);
+  }
+
+  *keyBlob = kmBlob2vector(response.upgraded_key);
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) {
+  DeleteKeyRequest request(impl_.message_version());
+  request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+  DeleteKeyResponse response(impl_.message_version());
+  impl_.DeleteKey(request, &response);
+
+  return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus RemoteKeyMintDevice::deleteAllKeys() {
+  // There's nothing to be done to delete software key blobs.
+  DeleteAllKeysRequest request(impl_.message_version());
+  DeleteAllKeysResponse response(impl_.message_version());
+  impl_.DeleteAllKeys(request, &response);
+
+  return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus RemoteKeyMintDevice::destroyAttestationIds() {
+  return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus RemoteKeyMintDevice::begin(KeyPurpose purpose,
+                                         const vector<uint8_t>& keyBlob,
+                                         const vector<KeyParameter>& params,
+                                         const HardwareAuthToken& authToken,
+                                         BeginResult* result) {
+  BeginOperationRequest request(impl_.message_version());
+  request.purpose = legacy_enum_conversion(purpose);
+  request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+  request.additional_params.Reinitialize(KmParamSet(params));
+
+  vector<uint8_t> vector_token = authToken2AidlVec(authToken);
+  request.additional_params.push_back(
+      TAG_AUTH_TOKEN, reinterpret_cast<uint8_t*>(vector_token.data()),
+      vector_token.size());
+
+  BeginOperationResponse response(impl_.message_version());
+  impl_.BeginOperation(request, &response);
+
+  if (response.error != KM_ERROR_OK) {
+    return kmError2ScopedAStatus(response.error);
+  }
+
+  result->params = kmParamSet2Aidl(response.output_params);
+  result->challenge = response.op_handle;
+  result->operation = ndk::SharedRefBase::make<RemoteKeyMintOperation>(
+      impl_, response.op_handle);
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintDevice::deviceLocked(
+    bool passwordOnly,
+    const std::optional<secureclock::TimeStampToken>& timestampToken) {
+  DeviceLockedRequest request(impl_.message_version());
+  request.passwordOnly = passwordOnly;
+  if (timestampToken.has_value()) {
+    request.token.challenge = timestampToken->challenge;
+    request.token.mac = {timestampToken->mac.data(),
+                         timestampToken->mac.size()};
+    request.token.timestamp = timestampToken->timestamp.milliSeconds;
+  }
+  DeviceLockedResponse response = impl_.DeviceLocked(request);
+  return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus RemoteKeyMintDevice::earlyBootEnded() {
+  EarlyBootEndedResponse response = impl_.EarlyBootEnded();
+  return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus RemoteKeyMintDevice::convertStorageKeyToEphemeral(
+    const std::vector<uint8_t>& /* storageKeyBlob */,
+    std::vector<uint8_t>* /* ephemeralKeyBlob */) {
+  return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus RemoteKeyMintDevice::performOperation(
+    const vector<uint8_t>& /* request */, vector<uint8_t>* /* response */) {
+  return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/guest/hals/keymint/remote/remote_keymint_device.h b/guest/hals/keymint/remote/remote_keymint_device.h
new file mode 100644
index 0000000..7f289b4
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_keymint_device.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+
+#include "guest/hals/keymint/remote/remote_keymaster.h"
+
+namespace aidl::android::hardware::security::keymint {
+using ::ndk::ScopedAStatus;
+using std::optional;
+using std::shared_ptr;
+using std::vector;
+
+using secureclock::TimeStampToken;
+
+class RemoteKeyMintDevice : public BnKeyMintDevice {
+ public:
+  explicit RemoteKeyMintDevice(::keymaster::RemoteKeymaster&, SecurityLevel);
+  virtual ~RemoteKeyMintDevice();
+
+  ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
+
+  ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
+
+  ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
+                            const optional<AttestationKey>& attestationKey,
+                            KeyCreationResult* creationResult) override;
+
+  ScopedAStatus importKey(const vector<KeyParameter>& keyParams,
+                          KeyFormat keyFormat, const vector<uint8_t>& keyData,
+                          const optional<AttestationKey>& attestationKey,
+                          KeyCreationResult* creationResult) override;
+
+  ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+                                 const vector<uint8_t>& wrappingKeyBlob,
+                                 const vector<uint8_t>& maskingKey,
+                                 const vector<KeyParameter>& unwrappingParams,
+                                 int64_t passwordSid, int64_t biometricSid,
+                                 KeyCreationResult* creationResult) override;
+
+  ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+                           const vector<KeyParameter>& upgradeParams,
+                           vector<uint8_t>* keyBlob) override;
+
+  ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
+  ScopedAStatus deleteAllKeys() override;
+  ScopedAStatus destroyAttestationIds() override;
+
+  ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
+                      const vector<KeyParameter>& params,
+                      const HardwareAuthToken& authToken,
+                      BeginResult* result) override;
+
+  ScopedAStatus deviceLocked(
+      bool passwordOnly,
+      const optional<TimeStampToken>& timestampToken) override;
+  ScopedAStatus earlyBootEnded() override;
+
+  ScopedAStatus convertStorageKeyToEphemeral(
+      const std::vector<uint8_t>& storageKeyBlob,
+      std::vector<uint8_t>* ephemeralKeyBlob) override;
+
+  ScopedAStatus performOperation(const vector<uint8_t>& request,
+                                 vector<uint8_t>* response) override;
+
+ protected:
+  ::keymaster::RemoteKeymaster& impl_;
+  SecurityLevel securityLevel_;
+};
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/guest/hals/keymint/remote/remote_keymint_operation.cpp b/guest/hals/keymint/remote/remote_keymint_operation.cpp
new file mode 100644
index 0000000..a5fb6aa
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_keymint_operation.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "android.hardware.security.keymint-impl"
+#include <log/log.h>
+
+#include "guest/hals/keymint/remote/remote_keymint_operation.h"
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+
+#include <keymaster/android_keymaster.h>
+
+#include "KeyMintUtils.h"
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::Buffer;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::TAG_ASSOCIATED_DATA;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using secureclock::TimeStampToken;
+using namespace km_utils;
+
+RemoteKeyMintOperation::RemoteKeyMintOperation(
+    ::keymaster::RemoteKeymaster& impl, keymaster_operation_handle_t opHandle)
+    : impl_(impl), opHandle_(opHandle) {}
+
+RemoteKeyMintOperation::~RemoteKeyMintOperation() {
+  if (opHandle_ != 0) {
+    abort();
+  }
+}
+
+ScopedAStatus RemoteKeyMintOperation::updateAad(
+    const vector<uint8_t>& input,
+    const optional<HardwareAuthToken>& /* authToken */,
+    const optional<TimeStampToken>& /* timestampToken */) {
+  UpdateOperationRequest request(impl_.message_version());
+  request.op_handle = opHandle_;
+  request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(),
+                                      input.size());
+
+  UpdateOperationResponse response(impl_.message_version());
+  impl_.UpdateOperation(request, &response);
+
+  return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus RemoteKeyMintOperation::update(
+    const vector<uint8_t>& input,
+    const optional<HardwareAuthToken>& /* authToken */,
+    const optional<TimeStampToken>&
+    /* timestampToken */,
+    vector<uint8_t>* output) {
+  if (!output) return kmError2ScopedAStatus(KM_ERROR_OUTPUT_PARAMETER_NULL);
+
+  UpdateOperationRequest request(impl_.message_version());
+  request.op_handle = opHandle_;
+  request.input.Reinitialize(input.data(), input.size());
+
+  UpdateOperationResponse response(impl_.message_version());
+  impl_.UpdateOperation(request, &response);
+
+  if (response.error != KM_ERROR_OK)
+    return kmError2ScopedAStatus(response.error);
+  if (response.input_consumed != request.input.buffer_size()) {
+    return kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+  }
+
+  *output = kmBuffer2vector(response.output);
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintOperation::finish(
+    const optional<vector<uint8_t>>& input,      //
+    const optional<vector<uint8_t>>& signature,  //
+    const optional<HardwareAuthToken>& /* authToken */,
+    const optional<TimeStampToken>& /* timestampToken */,
+    const optional<vector<uint8_t>>& /* confirmationToken */,
+    vector<uint8_t>* output) {
+  if (!output) {
+    return ScopedAStatus(AStatus_fromServiceSpecificError(
+        static_cast<int32_t>(ErrorCode::OUTPUT_PARAMETER_NULL)));
+  }
+
+  FinishOperationRequest request(impl_.message_version());
+  request.op_handle = opHandle_;
+  if (input) request.input.Reinitialize(input->data(), input->size());
+  if (signature)
+    request.signature.Reinitialize(signature->data(), signature->size());
+
+  FinishOperationResponse response(impl_.message_version());
+  impl_.FinishOperation(request, &response);
+  opHandle_ = 0;
+
+  if (response.error != KM_ERROR_OK)
+    return kmError2ScopedAStatus(response.error);
+
+  *output = kmBuffer2vector(response.output);
+  return ScopedAStatus::ok();
+}
+
+ScopedAStatus RemoteKeyMintOperation::abort() {
+  AbortOperationRequest request(impl_.message_version());
+  request.op_handle = opHandle_;
+
+  AbortOperationResponse response(impl_.message_version());
+  impl_.AbortOperation(request, &response);
+  opHandle_ = 0;
+
+  return kmError2ScopedAStatus(response.error);
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/guest/hals/keymint/remote/remote_keymint_operation.h b/guest/hals/keymint/remote/remote_keymint_operation.h
new file mode 100644
index 0000000..eb3c78b
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_keymint_operation.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+
+#include <hardware/keymaster_defs.h>
+
+#include "guest/hals/keymint/remote/remote_keymaster.h"
+
+namespace keymaster {
+class AndroidKeymaster;
+}
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using std::optional;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+class RemoteKeyMintOperation : public BnKeyMintOperation {
+ public:
+  explicit RemoteKeyMintOperation(::keymaster::RemoteKeymaster& implementation,
+                                  keymaster_operation_handle_t opHandle);
+  virtual ~RemoteKeyMintOperation();
+
+  ScopedAStatus updateAad(
+      const vector<uint8_t>& input,
+      const optional<HardwareAuthToken>& authToken,
+      const optional<TimeStampToken>& timestampToken) override;
+
+  ScopedAStatus update(const vector<uint8_t>& input,
+                       const optional<HardwareAuthToken>& authToken,
+                       const optional<TimeStampToken>& timestampToken,
+                       vector<uint8_t>* output) override;
+
+  ScopedAStatus finish(const optional<vector<uint8_t>>& input,        //
+                       const optional<vector<uint8_t>>& signature,    //
+                       const optional<HardwareAuthToken>& authToken,  //
+                       const optional<TimeStampToken>& timestampToken,
+                       const optional<vector<uint8_t>>& confirmationToken,
+                       vector<uint8_t>* output) override;
+
+  ScopedAStatus abort() override;
+
+ protected:
+  ::keymaster::RemoteKeymaster& impl_;
+  keymaster_operation_handle_t opHandle_;
+};
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/guest/hals/keymint/remote/remote_secure_clock.cpp b/guest/hals/keymint/remote/remote_secure_clock.cpp
new file mode 100644
index 0000000..53bab78
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_secure_clock.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "android.hardware.security.secureclock-impl"
+#include <log/log.h>
+
+#include "guest/hals/keymint/remote/remote_secure_clock.h"
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/keymaster_configuration.h>
+#include "KeyMintUtils.h"
+
+namespace aidl::android::hardware::security::secureclock {
+
+using namespace ::keymaster;
+using namespace ::aidl::android::hardware::security::keymint::km_utils;
+
+RemoteSecureClock::RemoteSecureClock(keymaster::RemoteKeymaster& impl)
+    : impl_(impl) {}
+
+RemoteSecureClock::~RemoteSecureClock() {}
+
+ScopedAStatus RemoteSecureClock::generateTimeStamp(int64_t challenge,
+                                                   TimeStampToken* token) {
+  GenerateTimestampTokenRequest request(impl_.message_version());
+  request.challenge = challenge;
+  GenerateTimestampTokenResponse response(request.message_version);
+  impl_.GenerateTimestampToken(request, &response);
+  if (response.error != KM_ERROR_OK) {
+    return kmError2ScopedAStatus(response.error);
+  }
+  token->challenge = response.token.challenge;
+  token->timestamp.milliSeconds =
+      static_cast<int64_t>(response.token.timestamp);
+  token->mac = kmBlob2vector(response.token.mac);
+  return ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::security::secureclock
diff --git a/guest/hals/keymint/remote/remote_secure_clock.h b/guest/hals/keymint/remote/remote_secure_clock.h
new file mode 100644
index 0000000..a0330e2
--- /dev/null
+++ b/guest/hals/keymint/remote/remote_secure_clock.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/secureclock/BnSecureClock.h>
+#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
+#include <aidl/android/hardware/security/secureclock/Timestamp.h>
+#include "guest/hals/keymint/remote/remote_keymaster.h"
+
+namespace keymaster {
+class AndroidKeymaster;
+}
+
+namespace aidl::android::hardware::security::secureclock {
+using ::ndk::ScopedAStatus;
+using std::shared_ptr;
+using std::vector;
+
+class RemoteSecureClock : public BnSecureClock {
+ public:
+  explicit RemoteSecureClock(::keymaster::RemoteKeymaster& keymint);
+  virtual ~RemoteSecureClock();
+  ScopedAStatus generateTimeStamp(int64_t challenge,
+                                  TimeStampToken* token) override;
+
+ private:
+  ::keymaster::RemoteKeymaster& impl_;
+};
+}  // namespace aidl::android::hardware::security::secureclock
diff --git a/guest/hals/keymint/remote/service.cpp b/guest/hals/keymint/remote/service.cpp
new file mode 100644
index 0000000..0591edf
--- /dev/null
+++ b/guest/hals/keymint/remote/service.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "android.hardware.security.keymint-service"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <keymaster/soft_keymaster_logger.h>
+#include "guest/hals/keymint/remote/remote_keymint_device.h"
+
+#include <guest/hals/keymint/remote/remote_keymaster.h>
+#include <guest/hals/keymint/remote/remote_keymint_device.h>
+#include <guest/hals/keymint/remote/remote_secure_clock.h>
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/security/keymaster_channel.h"
+
+static const char device[] = "/dev/hvc3";
+
+using aidl::android::hardware::security::keymint::RemoteKeyMintDevice;
+using aidl::android::hardware::security::keymint::SecurityLevel;
+using aidl::android::hardware::security::secureclock::RemoteSecureClock;
+
+template <typename T, class... Args>
+static std::shared_ptr<T> addService(Args&&... args) {
+  std::shared_ptr<T> ser =
+      ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
+  auto instanceName = std::string(T::descriptor) + "/remote";
+  LOG(INFO) << "adding keymint service instance: " << instanceName;
+  binder_status_t status =
+      AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
+  CHECK(status == STATUS_OK);
+  return ser;
+}
+
+int main() {
+  // Zero threads seems like a useless pool, but below we'll join this thread to
+  // it, increasing the pool size to 1.
+  ABinderProcess_setThreadPoolMaxThreadCount(0);
+  // Add Keymint Service
+  auto fd = cuttlefish::SharedFD::Open(device, O_RDWR);
+  if (!fd->IsOpen()) {
+    LOG(FATAL) << "Could not connect to keymaster: " << fd->StrError();
+  }
+
+  if (fd->SetTerminalRaw() < 0) {
+    LOG(FATAL) << "Could not make " << device
+               << " a raw terminal: " << fd->StrError();
+  }
+
+  cuttlefish::KeymasterChannel keymasterChannel(fd, fd);
+
+  keymaster::RemoteKeymaster remote_keymaster(&keymasterChannel);
+
+  addService<RemoteKeyMintDevice>(remote_keymaster,
+                                  SecurityLevel::TRUSTED_ENVIRONMENT);
+  addService<RemoteSecureClock>(remote_keymaster);
+
+  ABinderProcess_joinThreadPool();
+  return EXIT_FAILURE;  // should not reach
+}
diff --git a/guest/hals/ril/reference-libril/ril.h b/guest/hals/ril/reference-libril/ril.h
index 8942bcc..586de42 100644
--- a/guest/hals/ril/reference-libril/ril.h
+++ b/guest/hals/ril/reference-libril/ril.h
@@ -7537,7 +7537,14 @@
  */
 #define RIL_REQUEST_GET_SLICING_CONFIG 169
 
-#define RIL_REQUEST_LAST RIL_REQUEST_GET_SLICING_CONFIG
+#define RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS 170
+
+#define RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY 171
+
+#define RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS 172
+
+
+#define RIL_REQUEST_LAST RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS
 
 /***********************************************************************/
 
diff --git a/guest/hals/ril/reference-libril/ril_commands.h b/guest/hals/ril/reference-libril/ril_commands.h
index 4cb950b..81629b0 100644
--- a/guest/hals/ril/reference-libril/ril_commands.h
+++ b/guest/hals/ril/reference-libril/ril_commands.h
@@ -183,5 +183,7 @@
     {RIL_REQUEST_SET_DATA_THROTTLING, radio_1_6::setDataThrottlingResponse},
     {RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, radio_1_6::getSystemSelectionChannelsResponse},
     {RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP, radio_1_6::getAllowedNetworkTypesBitmapResponse},
-    {RIL_REQUEST_GET_SLICING_CONFIG, radio_1_6::getSlicingConfigResponse}
-
+    {RIL_REQUEST_GET_SLICING_CONFIG, radio_1_6::getSlicingConfigResponse},
+    {RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, radio_1_6::getSimPhonebookRecordsResponse},
+    {RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, radio_1_6::getSimPhonebookCapacityResponse},
+    {RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS, radio_1_6::updateSimPhonebookRecordsResponse}
diff --git a/guest/hals/ril/reference-libril/ril_service.cpp b/guest/hals/ril/reference-libril/ril_service.cpp
index 635d48f..848d4dd 100644
--- a/guest/hals/ril/reference-libril/ril_service.cpp
+++ b/guest/hals/ril/reference-libril/ril_service.cpp
@@ -646,6 +646,11 @@
     Return<void> setCarrierInfoForImsiEncryption_1_6(
             int32_t serial,
             const ::android::hardware::radio::V1_6::ImsiEncryptionInfo& imsiEncryptionInfo);
+    Return<void> getSimPhonebookRecords(int32_t serial);
+    Return<void> getSimPhonebookCapacity(int32_t serial);
+    Return<void> updateSimPhonebookRecords(
+            int32_t serial,
+            const ::android::hardware::radio::V1_6::PhonebookRecordInfo& recordInfo);
 };
 
 struct OemHookImpl : public IOemHook {
@@ -4696,6 +4701,34 @@
     return Void();
 }
 
+
+Return<void> RadioImpl_1_6::getSimPhonebookRecords(int32_t serial) {
+#if VDBG
+    RLOGD("getSimPhonebookRecords: serial %d", serial);
+#endif
+    dispatchVoid(serial, mSlotId, RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS);
+    return Void();
+}
+
+Return<void> RadioImpl_1_6::getSimPhonebookCapacity(int32_t serial) {
+#if VDBG
+    RLOGD("getSimPhonebookCapacity: serial %d", serial);
+#endif
+    dispatchVoid(serial, mSlotId, RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY);
+    return Void();
+}
+
+Return<void> RadioImpl_1_6::updateSimPhonebookRecords(
+    int32_t serial,
+    const ::android::hardware::radio::V1_6::PhonebookRecordInfo& recordInfo) {
+#if VDBG
+    RLOGD("updateSimPhonebookRecords: serial %d", serial);
+#endif
+    dispatchVoid(serial, mSlotId, RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS);
+    return Void();
+}
+
+
 // OEM hook methods:
 Return<void> OemHookImpl::setResponseFunctions(
         const ::android::sp<IOemHookResponse>& oemHookResponseParam,
@@ -10303,6 +10336,31 @@
 
     return 0;
 }
+
+int radio_1_6::getSimPhonebookRecordsResponse(int slotId, int responseType, int serial,
+                             RIL_Errno e, void *response, size_t responseLen) {
+#if VDBG
+    RLOGD("getSimPhonebookRecordsResponse: serial %d", serial);
+#endif
+    return 0;
+}
+
+int radio_1_6::getSimPhonebookCapacityResponse(int slotId, int responseType, int serial,
+                             RIL_Errno e, void *response, size_t responseLen) {
+#if VDBG
+    RLOGD("getSimPhonebookRecordsResponse: serial %d", serial);
+#endif
+    return 0;
+}
+
+int radio_1_6::updateSimPhonebookRecordsResponse(int slotId, int responseType, int serial,
+                             RIL_Errno e, void *response, size_t responseLen) {
+#if VDBG
+    RLOGD("getSimPhonebookRecordsResponse: serial %d", serial);
+#endif
+    return 0;
+}
+
 /***************************************************************************************************
  * INDICATION FUNCTIONS
  * The below function handle unsolicited messages coming from the Radio
diff --git a/guest/hals/ril/reference-libril/ril_service.h b/guest/hals/ril/reference-libril/ril_service.h
index 85bd091..30c7b69 100644
--- a/guest/hals/ril/reference-libril/ril_service.h
+++ b/guest/hals/ril/reference-libril/ril_service.h
@@ -821,6 +821,15 @@
 int getSlicingConfigResponse(int slotId, int responseType, int serial,
                              RIL_Errno e, void *response, size_t responseLen);
 
+int getSimPhonebookRecordsResponse(int slotId, int responseType, int serial,
+                             RIL_Errno e, void *response, size_t responseLen);
+
+int getSimPhonebookCapacityResponse(int slotId, int responseType, int serial,
+                             RIL_Errno e, void *response, size_t responseLen);
+
+int updateSimPhonebookRecordsResponse(int slotId, int responseType, int serial,
+                             RIL_Errno e, void *response, size_t responseLen);
+
 
 pthread_rwlock_t * getRadioServiceRwlock(int slotId);
 
diff --git a/guest/hals/ril/reference-ril/reference-ril.c b/guest/hals/ril/reference-ril/reference-ril.c
index 6c00d97..97b048a 100644
--- a/guest/hals/ril/reference-ril/reference-ril.c
+++ b/guest/hals/ril/reference-ril/reference-ril.c
@@ -325,7 +325,6 @@
 
 static RIL_RadioState sState = RADIO_STATE_UNAVAILABLE;
 static bool isNrDualConnectivityEnabled = true;
-static unsigned int allowedNetworkTypesBitmap = UINT_MAX;
 
 static pthread_mutex_t s_state_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t s_state_cond = PTHREAD_COND_INITIALIZER;
@@ -1450,7 +1449,8 @@
             return;
     }
 done:
-    if (request == RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE_BITMAP) {
+    if (request == RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE_BITMAP ||
+            request == RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP) {
         i = getBitmapFromPreferred(preferred);
     }
     RIL_onRequestComplete(t, RIL_E_SUCCESS, &i, sizeof(i));
@@ -4865,17 +4865,10 @@
             requestSetPreferredNetworkType(request, data, datalen, t);
             break;
         case RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP:
-            if (data == NULL || datalen != sizeof(int)) {
-              RIL_onRequestComplete(t, RIL_E_INTERNAL_ERR, NULL, 0);
-              break;
-            }
-            allowedNetworkTypesBitmap = *(int *)data;
-            RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+            requestSetPreferredNetworkType(request, data, datalen, t);
             break;
         case RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP:
-            RIL_onRequestComplete(t, RIL_E_SUCCESS, &allowedNetworkTypesBitmap,
-                    sizeof(allowedNetworkTypesBitmap));
-            break;
+            requestGetPreferredNetworkType(request, data, datalen, t);
         case RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY:
             if (data == NULL || datalen != sizeof(int)) {
                 RIL_onRequestComplete(t, RIL_E_INTERNAL_ERR, NULL, 0);
diff --git a/host/commands/assemble_cvd/assemble_cvd.cc b/host/commands/assemble_cvd/assemble_cvd.cc
index fec1ecf..1350bb6 100644
--- a/host/commands/assemble_cvd/assemble_cvd.cc
+++ b/host/commands/assemble_cvd/assemble_cvd.cc
@@ -102,7 +102,7 @@
 #endif
 
 const CuttlefishConfig* InitFilesystemAndCreateConfig(
-    FetcherConfig fetcher_config) {
+    FetcherConfig fetcher_config, KernelConfig kernel_config) {
   std::string assembly_dir_parent = AbsolutePath(FLAGS_assembly_dir);
   while (assembly_dir_parent[assembly_dir_parent.size() - 1] == '/') {
     assembly_dir_parent =
@@ -131,7 +131,7 @@
     // two operations, as those will assume they can read the config object from
     // disk.
     auto config = InitializeCuttlefishConfiguration(
-        FLAGS_assembly_dir, FLAGS_instance_dir, FLAGS_modem_simulator_count);
+        FLAGS_instance_dir, FLAGS_modem_simulator_count, kernel_config);
     std::set<std::string> preserving;
     if (FLAGS_resume && ShouldCreateAllCompositeDisks(config)) {
       LOG(INFO) << "Requested resuming a previous session (the default behavior) "
@@ -139,20 +139,19 @@
                 << "overlay incompatible. Wiping the overlay files.";
     } else if (FLAGS_resume && !ShouldCreateAllCompositeDisks(config)) {
       preserving.insert("overlay.img");
-      preserving.insert("gpt_header.img");
-      preserving.insert("gpt_footer.img");
-      preserving.insert("composite.img");
+      preserving.insert("os_composite_disk_config.txt");
+      preserving.insert("os_composite_gpt_header.img");
+      preserving.insert("os_composite_gpt_footer.img");
+      preserving.insert("os_composite.img");
       preserving.insert("sdcard.img");
       preserving.insert("uboot_env.img");
       preserving.insert("boot_repacked.img");
       preserving.insert("vendor_boot_repacked.img");
       preserving.insert("access-kregistry");
-      preserving.insert("disk_hole");
       preserving.insert("NVChip");
       preserving.insert("gatekeeper_secure");
       preserving.insert("gatekeeper_insecure");
       preserving.insert("modem_nvram.json");
-      preserving.insert("disk_config.txt");
       preserving.insert("recording");
       preserving.insert("factory_reset_protected.img");
       std::stringstream ss;
@@ -173,8 +172,6 @@
                   << config.AssemblyPath("assemble_cvd.log")
                   << ": " << log->StrError();
     }
-    std::string disk_hole_dir = FLAGS_assembly_dir + "/disk_hole";
-    CHECK(EnsureDirectoryExists(disk_hole_dir));
     for (const auto& instance : config.Instances()) {
       // Create instance directory if it doesn't exist.
       CHECK(EnsureDirectoryExists(instance.instance_dir()));
@@ -203,6 +200,21 @@
   return config;
 }
 
+const std::string kKernelDefaultPath = "kernel";
+const std::string kInitramfsImg = "initramfs.img";
+static void ExtractKernelParamsFromFetcherConfig(
+    const FetcherConfig& fetcher_config) {
+  std::string discovered_kernel =
+      fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath);
+  std::string discovered_ramdisk =
+      fetcher_config.FindCvdFileWithSuffix(kInitramfsImg);
+
+  SetCommandLineOptionWithMode("kernel_path", discovered_kernel.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+
+  SetCommandLineOptionWithMode("initramfs_path", discovered_ramdisk.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
+}
 } // namespace
 
 int AssembleCvdMain(int argc, char** argv) {
@@ -227,9 +239,15 @@
   }
   std::vector<std::string> input_files = android::base::Split(input_files_str, "\n");
 
-  CHECK(ParseCommandLineFlags(&argc, &argv)) << "Failed to parse arguments";
+  FetcherConfig fetcher_config = FindFetcherConfig(input_files);
+  // set gflags defaults to point to kernel/RD from fetcher config
+  ExtractKernelParamsFromFetcherConfig(fetcher_config);
 
-  auto config = InitFilesystemAndCreateConfig(FindFetcherConfig(input_files));
+  KernelConfig kernel_config;
+  CHECK(ParseCommandLineFlags(&argc, &argv, &kernel_config)) << "Failed to parse arguments";
+
+  auto config =
+      InitFilesystemAndCreateConfig(std::move(fetcher_config), kernel_config);
 
   std::cout << GetConfigFilePath(*config) << "\n";
   std::cout << std::flush;
diff --git a/host/commands/assemble_cvd/boot_config.cc b/host/commands/assemble_cvd/boot_config.cc
index a6d41dd..b433940 100644
--- a/host/commands/assemble_cvd/boot_config.cc
+++ b/host/commands/assemble_cvd/boot_config.cc
@@ -50,9 +50,6 @@
   if (!config.boot_slot().empty()) {
       env << "android_slot_suffix=_" << config.boot_slot() << '\0';
   }
-  // Points to the misc partition.
-  // Note that the 0 index points to the GPT table.
-  env << "bootdevice=0:2" << '\0';
 
   if(FLAGS_pause_in_bootloader) {
     env << "bootdelay=-1" << '\0';
@@ -60,9 +57,10 @@
     env << "bootdelay=0" << '\0';
   }
 
-  env << "bootcmd=boot_android virtio -" << '\0';
+  // Note that the 0 index points to the GPT table.
+  env << "bootcmd=boot_android virtio 0#misc" << '\0';
   if (FLAGS_vm_manager == CrosvmManager::name() &&
-          HostArch() == "aarch64") {
+      config.target_arch() == Arch::Arm64) {
     env << "fdtaddr=0x80000000" << '\0';
   } else {
     env << "fdtaddr=0x40000000" << '\0';
diff --git a/host/commands/assemble_cvd/boot_image_utils.cc b/host/commands/assemble_cvd/boot_image_utils.cc
index 5654f93..a222f85 100644
--- a/host/commands/assemble_cvd/boot_image_utils.cc
+++ b/host/commands/assemble_cvd/boot_image_utils.cc
@@ -179,14 +179,6 @@
 
 }  // namespace
 
-std::string ExtractKernelFromBootImage(const std::string& boot_image_path,
-                                       const std::string& unpack_dir) {
-  if (UnpackBootImage(boot_image_path, unpack_dir)) {
-    return unpack_dir + "/kernel";
-  } else {
-    return "";
-  }
-}
 bool RepackBootImage(const std::string& new_kernel_path,
                      const std::string& boot_image_path,
                      const std::string& new_boot_image_path,
diff --git a/host/commands/assemble_cvd/boot_image_utils.h b/host/commands/assemble_cvd/boot_image_utils.h
index ae28761..fafb514 100644
--- a/host/commands/assemble_cvd/boot_image_utils.h
+++ b/host/commands/assemble_cvd/boot_image_utils.h
@@ -19,8 +19,6 @@
 #include <vector>
 
 namespace cuttlefish {
-std::string ExtractKernelFromBootImage(const std::string& boot_image_path,
-                                       const std::string& unpack_dir);
 bool RepackBootImage(const std::string& new_kernel_path,
                      const std::string& boot_image_path,
                      const std::string& new_boot_image_path,
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index fd9a1dd..21a5cca 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -118,7 +118,7 @@
   return true;
 }
 
-std::vector<ImagePartition> disk_config(
+std::vector<ImagePartition> os_composite_disk_config(
     const CuttlefishConfig::InstanceSpecific& instance) {
   std::vector<ImagePartition> partitions;
 
@@ -201,9 +201,9 @@
 }
 
 static std::chrono::system_clock::time_point LastUpdatedInputDisk(
-    const CuttlefishConfig::InstanceSpecific& instance) {
+    const std::vector<ImagePartition>& partitions) {
   std::chrono::system_clock::time_point ret;
-  for (auto& partition : disk_config(instance)) {
+  for (auto& partition : partitions) {
     auto partition_mod_time = FileModificationTime(partition.image_file_path);
     if (partition_mod_time > ret) {
       ret = partition_mod_time;
@@ -214,7 +214,8 @@
 
 bool ShouldCreateAllCompositeDisks(const CuttlefishConfig& config) {
   std::chrono::system_clock::time_point youngest_disk_img;
-  for (auto& partition : disk_config(config.ForDefaultInstance())) {
+  for (auto& partition :
+       os_composite_disk_config(config.ForDefaultInstance())) {
     if (partition.label == "uboot_env") {
       continue;
     }
@@ -228,10 +229,11 @@
   // If the youngest partition img is younger than any composite disk, this fact implies that
   // the composite disks are all out of date and need to be reinitialized.
   for (auto& instance : config.Instances()) {
-    if (!FileExists(instance.composite_disk_path())) {
+    if (!FileExists(instance.os_composite_disk_path())) {
       continue;
     }
-    if (youngest_disk_img > FileModificationTime(instance.composite_disk_path())) {
+    if (youngest_disk_img >
+        FileModificationTime(instance.os_composite_disk_path())) {
       return true;
     }
   }
@@ -240,11 +242,11 @@
 }
 
 bool DoesCompositeMatchCurrentDiskConfig(
-    const CuttlefishConfig::InstanceSpecific& instance) {
-  std::string prior_disk_config_path = instance.PerInstancePath("disk_config.txt");
-  std::string current_disk_config_path = instance.PerInstancePath("disk_config.txt.tmp");
+    const std::string& prior_disk_config_path,
+    const std::vector<ImagePartition>& partitions) {
+  std::string current_disk_config_path = prior_disk_config_path + ".tmp";
   std::ostringstream disk_conf;
-  for (auto& partition : disk_config(instance)) {
+  for (auto& partition : partitions) {
     disk_conf << partition.image_file_path << "\n";
   }
 
@@ -269,13 +271,14 @@
   }
 }
 
-bool ShouldCreateCompositeDisk(const CuttlefishConfig::InstanceSpecific& instance) {
-  if (!FileExists(instance.composite_disk_path())) {
+bool ShouldCreateCompositeDisk(const std::string& composite_disk_path,
+                               const std::vector<ImagePartition>& partitions) {
+  if (!FileExists(composite_disk_path)) {
     return true;
   }
 
-  auto composite_age = FileModificationTime(instance.composite_disk_path());
-  return composite_age < LastUpdatedInputDisk(instance);
+  auto composite_age = FileModificationTime(composite_disk_path);
+  return composite_age < LastUpdatedInputDisk(partitions);
 }
 
 static uint64_t AvailableSpaceAtPath(const std::string& path) {
@@ -292,9 +295,11 @@
 
 bool CreateCompositeDisk(const CuttlefishConfig& config,
                          const CuttlefishConfig::InstanceSpecific& instance) {
-  if (!SharedFD::Open(instance.composite_disk_path().c_str(),
-                                  O_WRONLY | O_CREAT, 0644)->IsOpen()) {
-    LOG(ERROR) << "Could not ensure " << instance.composite_disk_path() << " exists";
+  if (!SharedFD::Open(instance.os_composite_disk_path().c_str(),
+                      O_WRONLY | O_CREAT, 0644)
+           ->IsOpen()) {
+    LOG(ERROR) << "Could not ensure " << instance.os_composite_disk_path()
+               << " exists";
     return false;
   }
   if (config.vm_manager() == CrosvmManager::name()) {
@@ -318,63 +323,22 @@
       LOG(DEBUG) << "Disk size of \"" << FLAGS_data_image << "\": "
                  << existing_sizes.disk_size;
     }
-    std::string header_path = instance.PerInstancePath("gpt_header.img");
-    std::string footer_path = instance.PerInstancePath("gpt_footer.img");
-    CreateCompositeDisk(disk_config(instance), header_path, footer_path,
-                        instance.composite_disk_path());
+    std::string header_path =
+        instance.PerInstancePath("os_composite_gpt_header.img");
+    std::string footer_path =
+        instance.PerInstancePath("os_composite_gpt_footer.img");
+    CreateCompositeDisk(os_composite_disk_config(instance), header_path,
+                        footer_path, instance.os_composite_disk_path());
   } else {
     // If this doesn't fit into the disk, it will fail while aggregating. The
     // aggregator doesn't maintain any sparse attributes.
-    AggregateImage(disk_config(instance), instance.composite_disk_path());
+    AggregateImage(os_composite_disk_config(instance),
+                   instance.os_composite_disk_path());
   }
   return true;
 }
 
-static bool IsBootconfigSupported(const std::string& tmp_dir) {
-  const std::string kernel_image_path =
-      FLAGS_kernel_path.size()
-          ? FLAGS_kernel_path
-          : ExtractKernelFromBootImage(FLAGS_boot_image, tmp_dir);
-  const std::string ikconfig_path = tmp_dir + "/ikconfig";
-
-  Command ikconfig_cmd(HostBinaryPath("extract-ikconfig"));
-  ikconfig_cmd.AddParameter(kernel_image_path);
-
-  std::string current_path = StringFromEnv("PATH", "");
-  std::string bin_folder = DefaultHostArtifactsPath("bin");
-  ikconfig_cmd.SetEnvironment({"PATH=" + current_path + ":" + bin_folder});
-
-  auto ikconfig_fd = SharedFD::Creat(ikconfig_path, 0666);
-  CHECK(ikconfig_fd->IsOpen())
-      << "Unable to create ikconfig file: " << ikconfig_fd->StrError();
-  ikconfig_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, ikconfig_fd);
-
-  auto ikconfig_proc = ikconfig_cmd.Start();
-  CHECK(ikconfig_proc.Started() && ikconfig_proc.Wait() == 0)
-      << "Failed to extract ikconfig from " << kernel_image_path;
-
-  return ReadFile(ikconfig_path).find("BOOT_CONFIG=y") != std::string::npos;
-}
-
-const std::string kKernelDefaultPath = "kernel";
-const std::string kInitramfsImg = "initramfs.img";
-static void ExtractKernelParamsFromFetcherConfig(
-    const FetcherConfig& fetcher_config) {
-  std::string discovered_kernel =
-      fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath);
-  std::string discovered_ramdisk =
-      fetcher_config.FindCvdFileWithSuffix(kInitramfsImg);
-
-  SetCommandLineOptionWithMode("kernel_path", discovered_kernel.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-
-  SetCommandLineOptionWithMode("initramfs_path", discovered_ramdisk.c_str(),
-                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
-}
-
-static void RepackAllBootImages(const FetcherConfig& fetcher_config,
-                                const CuttlefishConfig* config) {
-  ExtractKernelParamsFromFetcherConfig(fetcher_config);
+static void RepackAllBootImages(const CuttlefishConfig* config) {
   CHECK(FileHasContent(FLAGS_boot_image))
       << "File not found: " << FLAGS_boot_image;
 
@@ -391,8 +355,6 @@
                                  google::FlagSettingMode::SET_FLAGS_DEFAULT);
   }
 
-  const bool bootconfig_supported =
-      IsBootconfigSupported(config->assembly_dir());
   for (auto instance : config->Instances()) {
     const std::string new_vendor_boot_image_path =
         instance.vendor_boot_image_path();
@@ -404,7 +366,8 @@
         bool success = RepackVendorBootImage(
             FLAGS_initramfs_path, FLAGS_vendor_boot_image,
             new_vendor_boot_image_path, config->assembly_dir(),
-            instance.instance_dir(), boot_config_vector, bootconfig_supported);
+            instance.instance_dir(), boot_config_vector,
+            config->bootconfig_supported());
         CHECK(success) << "Failed to regenerate the vendor boot image with the "
                           "new ramdisk";
       } else {
@@ -414,7 +377,7 @@
         bool success = RepackVendorBootImageWithEmptyRamdisk(
             FLAGS_vendor_boot_image, new_vendor_boot_image_path,
             config->assembly_dir(), instance.instance_dir(), boot_config_vector,
-            bootconfig_supported);
+            config->bootconfig_supported());
         CHECK(success)
             << "Failed to regenerate the vendor boot image without a ramdisk";
       }
@@ -424,7 +387,7 @@
       bool success = RepackVendorBootImage(
           std::string(), FLAGS_vendor_boot_image, new_vendor_boot_image_path,
           config->assembly_dir(), instance.instance_dir(), boot_config_vector,
-          bootconfig_supported);
+          config->bootconfig_supported());
       CHECK(success) << "Failed to regenerate the vendor boot image";
     }
   }
@@ -449,7 +412,7 @@
   // support. We can also assume that image repacking isn't trusted. Repacking
   // requires resigning the image and keys from an android host aren't trusted.
   if (!FLAGS_protected_vm) {
-    RepackAllBootImages(fetcher_config, config);
+    RepackAllBootImages(config);
 
     // TODO(b/181812679) remove this block when we no longer need to create the
     // env partition.
@@ -506,8 +469,11 @@
   bool newDataImage = dataImageResult == DataImageResult::FileUpdated;
 
   for (auto instance : config->Instances()) {
-    bool compositeMatchesDiskConfig = DoesCompositeMatchCurrentDiskConfig(instance);
-    bool oldCompositeDisk = ShouldCreateCompositeDisk(instance);
+    bool compositeMatchesDiskConfig = DoesCompositeMatchCurrentDiskConfig(
+        instance.PerInstancePath("os_composite_disk_config.txt"),
+        os_composite_disk_config(instance));
+    bool oldCompositeDisk = ShouldCreateCompositeDisk(
+        instance.os_composite_disk_path(), os_composite_disk_config(instance));
     if (!compositeMatchesDiskConfig || oldCompositeDisk || !FLAGS_resume || newDataImage) {
       if (FLAGS_resume) {
         LOG(INFO) << "Requested to continue an existing session, (the default) "
@@ -530,10 +496,11 @@
     for (auto instance : config->Instances()) {
       auto overlay_path = instance.PerInstancePath("overlay.img");
       bool missingOverlay = !FileExists(overlay_path);
-      bool newOverlay = FileModificationTime(overlay_path)
-          < FileModificationTime(instance.composite_disk_path());
-      if (!missingOverlay || !FLAGS_resume || newOverlay) {
-        CreateQcowOverlay(config->crosvm_binary(), instance.composite_disk_path(), overlay_path);
+      bool newOverlay = FileModificationTime(overlay_path) <
+                        FileModificationTime(instance.os_composite_disk_path());
+      if (missingOverlay || !FLAGS_resume || newOverlay) {
+        CreateQcowOverlay(config->crosvm_binary(),
+                          instance.os_composite_disk_path(), overlay_path);
       }
     }
   }
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 8dc71b0..bfb856b 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -72,9 +72,8 @@
               "Serial number to use for the device");
 DEFINE_bool(use_random_serial, false,
             "Whether to use random serial for the device.");
-DEFINE_string(
-    vm_manager, CrosvmManager::name(),
-    "What virtual machine manager to use, one of {qemu_cli, crosvm}");
+DEFINE_string(vm_manager, "",
+              "What virtual machine manager to use, one of {qemu_cli, crosvm}");
 DEFINE_string(gpu_mode, cuttlefish::kGpuModeAuto,
               "What gpu configuration to use, one of {auto, drm_virgl, "
               "gfxstream, guest_swiftshader}");
@@ -98,6 +97,14 @@
 DEFINE_bool(enable_host_bluetooth, true,
             "Enable the root-canal which is Bluetooth emulator in the host.");
 
+DEFINE_string(bluetooth_controller_properties_file,
+              "etc/rootcanal/data/controller_properties.json",
+              "The configuartion file path for root-canal which is a Bluetooth "
+              "emulator.");
+DEFINE_string(
+    bluetooth_default_commands_file, "etc/rootcanal/data/default_commands",
+    "The default commands which root-canal executes when it launches.");
+
 /**
  *
  * crosvm sandbox feature requires /var/empty and seccomp directory
@@ -120,7 +127,7 @@
             "Enable crosvm sandbox. Use this when you are sure about what you are doing.");
 
 static const std::string kSeccompDir =
-    std::string("usr/share/crosvm/") + cuttlefish::HostArch() + "-linux-gnu/seccomp";
+    std::string("usr/share/crosvm/") + cuttlefish::HostArchStr() + "-linux-gnu/seccomp";
 DEFINE_string(seccomp_policy_dir, DefaultHostArtifactsPath(kSeccompDir),
               "With sandbox'ed crosvm, overrieds the security comp policy directory");
 
@@ -210,9 +217,8 @@
 DEFINE_string(setupwizard_mode, "DISABLED",
             "One of DISABLED,OPTIONAL,REQUIRED");
 
-DEFINE_string(qemu_binary,
-              "/usr/bin/qemu-system-x86_64",
-              "The qemu binary to use");
+DEFINE_string(qemu_binary_dir, "/usr/bin",
+              "Path to the directory containing the qemu binary to use");
 DEFINE_string(crosvm_binary, HostBinaryPath("crosvm"),
               "The Crosvm binary to use");
 DEFINE_string(tpm_device, "", "A host TPM device to pass through commands to.");
@@ -290,20 +296,19 @@
 
 DEFINE_bool(protected_vm, false, "Boot in Protected VM mode");
 
-DECLARE_string(system_image_dir);
-
-DEFINE_bool(enable_audio, cuttlefish::HostArch() != "aarch64",
+DEFINE_bool(enable_audio, cuttlefish::HostArch() != cuttlefish::Arch::Arm64,
             "Whether to play or capture audio");
 
+DECLARE_string(assembly_dir);
+DECLARE_string(boot_image);
+DECLARE_string(system_image_dir);
+
 namespace cuttlefish {
 using vm_manager::QemuManager;
 using vm_manager::GetVmManager;
 
 namespace {
 
-const std::string kKernelDefaultPath = "kernel";
-const std::string kInitramfsImg = "initramfs.img";
-
 bool IsFlagSet(const std::string& flag) {
   return !gflags::GetCommandLineFlagInfoOrDie(flag.c_str()).is_default;
 }
@@ -332,17 +337,72 @@
   return stream.str();
 }
 
+#ifdef __ANDROID__
+void ReadKernelConfig(KernelConfig* kernel_config) {
+  // QEMU isn't on Android, so always follow host arch
+  kernel_config->target_arch = HostArch();
+  kernel_config->bootconfig_supported = true;
+}
+#else
+void ReadKernelConfig(KernelConfig* kernel_config) {
+  // extract-ikconfig can be called directly on the boot image since it looks
+  // for the ikconfig header in the image before extracting the config list.
+  // This code is liable to break if the boot image ever includes the
+  // ikconfig header outside the kernel.
+  const std::string kernel_image_path =
+      FLAGS_kernel_path.size() ? FLAGS_kernel_path : FLAGS_boot_image;
+
+  Command ikconfig_cmd(HostBinaryPath("extract-ikconfig"));
+  ikconfig_cmd.AddParameter(kernel_image_path);
+
+  std::string current_path = StringFromEnv("PATH", "");
+  std::string bin_folder = DefaultHostArtifactsPath("bin");
+  ikconfig_cmd.SetEnvironment({"PATH=" + current_path + ":" + bin_folder});
+
+  std::string ikconfig_path =
+      StringFromEnv("TEMP", "/tmp") + "/ikconfig.XXXXXX";
+  auto ikconfig_fd = SharedFD::Mkstemp(&ikconfig_path);
+  CHECK(ikconfig_fd->IsOpen())
+      << "Unable to create ikconfig file: " << ikconfig_fd->StrError();
+  ikconfig_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, ikconfig_fd);
+
+  auto ikconfig_proc = ikconfig_cmd.Start();
+  CHECK(ikconfig_proc.Started() && ikconfig_proc.Wait() == 0)
+      << "Failed to extract ikconfig from " << kernel_image_path;
+
+  std::string config = ReadFile(ikconfig_path);
+
+  if (config.find("\nCONFIG_ARM=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::Arm;
+  } else if (config.find("\nCONFIG_ARM64=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::Arm64;
+  } else if (config.find("\nCONFIG_X86_64=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::X86_64;
+  } else if (config.find("\nCONFIG_X86=y") != std::string::npos) {
+    kernel_config->target_arch = Arch::X86;
+  } else {
+    LOG(FATAL) << "Unknown target architecture";
+  }
+  kernel_config->bootconfig_supported =
+      config.find("\nCONFIG_BOOT_CONFIG=y") != std::string::npos;
+
+  unlink(ikconfig_path.c_str());
+}
+#endif  // #ifdef __ANDROID__
+
 } // namespace
 
 CuttlefishConfig InitializeCuttlefishConfiguration(
-    const std::string& assembly_dir, const std::string& instance_dir,
-    int modem_simulator_count) {
+    const std::string& instance_dir, int modem_simulator_count,
+    KernelConfig kernel_config) {
   // At most one streamer can be started.
   CHECK(NumStreamers() <= 1);
 
   CuttlefishConfig tmp_config_obj;
-  tmp_config_obj.set_assembly_dir(assembly_dir);
-  auto vmm = GetVmManager(FLAGS_vm_manager);
+  tmp_config_obj.set_assembly_dir(FLAGS_assembly_dir);
+  tmp_config_obj.set_target_arch(kernel_config.target_arch);
+  tmp_config_obj.set_bootconfig_supported(kernel_config.bootconfig_supported);
+  auto vmm = GetVmManager(FLAGS_vm_manager, kernel_config.target_arch);
   if (!vmm) {
     LOG(FATAL) << "Invalid vm_manager: " << FLAGS_vm_manager;
   }
@@ -441,7 +501,7 @@
 
   tmp_config_obj.set_deprecated_boot_completed(FLAGS_deprecated_boot_completed);
 
-  tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
+  tmp_config_obj.set_qemu_binary_dir(FLAGS_qemu_binary_dir);
   tmp_config_obj.set_crosvm_binary(FLAGS_crosvm_binary);
   tmp_config_obj.set_tpm_device(FLAGS_tpm_device);
 
@@ -636,13 +696,16 @@
     instance.set_rootcanal_hci_port(7300 + num - 1);
     instance.set_rootcanal_link_port(7400 + num - 1);
     instance.set_rootcanal_test_port(7500 + num - 1);
+    instance.set_rootcanal_config_file(
+        FLAGS_bluetooth_controller_properties_file);
+    instance.set_rootcanal_default_commands_file(
+        FLAGS_bluetooth_default_commands_file);
 
     instance.set_device_title(FLAGS_device_title);
 
     if (FLAGS_protected_vm) {
-      instance.set_virtual_disk_paths({
-        const_instance.PerInstancePath("composite.img")
-      });
+      instance.set_virtual_disk_paths(
+          {const_instance.PerInstancePath("os_composite.img")});
     } else {
       std::vector<std::string> virtual_disk_paths = {
         const_instance.PerInstancePath("overlay.img"),
@@ -702,9 +765,12 @@
   tmp_config_obj.set_enable_sandbox(FLAGS_enable_sandbox);
 
   // Audio is not available for VNC server
-  SetCommandLineOptionWithMode("enable_audio",
-                               FLAGS_start_vnc_server ? "false" : "true",
-                               SET_FLAGS_DEFAULT);
+  SetCommandLineOptionWithMode(
+      "enable_audio",
+      (FLAGS_start_vnc_server || (cuttlefish::HostArch() == cuttlefish::Arch::Arm64))
+          ? "false"
+          : "true",
+      SET_FLAGS_DEFAULT);
   tmp_config_obj.set_enable_audio(FLAGS_enable_audio);
 
   return tmp_config_obj;
@@ -801,7 +867,7 @@
 
   // for now, we support only x86_64 by default
   bool default_enable_sandbox = false;
-  std::set<const std::string> supported_archs{std::string("x86_64")};
+  std::set<Arch> supported_archs{Arch::X86_64};
   if (supported_archs.find(HostArch()) != supported_archs.end()) {
     if (DirectoryExists(kCrosvmVarEmptyDir)) {
       default_enable_sandbox = IsDirectoryEmpty(kCrosvmVarEmptyDir);
@@ -821,10 +887,25 @@
                                SET_FLAGS_DEFAULT);
 }
 
-bool ParseCommandLineFlags(int* argc, char*** argv) {
+bool ParseCommandLineFlags(int* argc, char*** argv, KernelConfig* kernel_config) {
   google::ParseCommandLineNonHelpFlags(argc, argv, true);
   SetDefaultFlagsFromConfigPreset();
+  google::HandleCommandLineHelpFlags();
   bool invalid_manager = false;
+
+  if (!ResolveInstanceFiles()) {
+    return false;
+  }
+
+  ReadKernelConfig(kernel_config);
+  if (FLAGS_vm_manager == "") {
+    if (IsHostCompatible(kernel_config->target_arch)) {
+      FLAGS_vm_manager = CrosvmManager::name();
+    } else {
+      FLAGS_vm_manager = QemuManager::name();
+    }
+  }
+
   if (FLAGS_vm_manager == QemuManager::name()) {
     SetDefaultFlagsForQemu();
   } else if (FLAGS_vm_manager == CrosvmManager::name()) {
@@ -839,14 +920,13 @@
   SetCommandLineOptionWithMode("start_webrtc_sig_server",
                                FLAGS_start_webrtc ? "true" : "false",
                                SET_FLAGS_DEFAULT);
-  google::HandleCommandLineHelpFlags();
   if (invalid_manager) {
     return false;
   }
   // Set the env variable to empty (in case the caller passed a value for it).
   unsetenv(kCuttlefishConfigEnvVarName);
 
-  return ResolveInstanceFiles();
+  return true;
 }
 
 std::string GetConfigFilePath(const CuttlefishConfig& config) {
diff --git a/host/commands/assemble_cvd/flags.h b/host/commands/assemble_cvd/flags.h
index 3dca9e3..3c02d92 100644
--- a/host/commands/assemble_cvd/flags.h
+++ b/host/commands/assemble_cvd/flags.h
@@ -3,16 +3,23 @@
 #include <cstdint>
 #include <optional>
 
+#include "common/libs/utils/environment.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/libs/config/fetcher_config.h"
 
 namespace cuttlefish {
 
-bool ParseCommandLineFlags(int* argc, char*** argv);
+struct KernelConfig {
+  Arch target_arch;
+  bool bootconfig_supported;
+};
+
+bool ParseCommandLineFlags(int* argc, char*** argv,
+                           KernelConfig* kernel_config);
 // Must be called after ParseCommandLineFlags.
 CuttlefishConfig InitializeCuttlefishConfiguration(
-    const std::string& assembly_dir, const std::string& instance_dir,
-    int modem_simulator_count);
+    const std::string& instance_dir, int modem_simulator_count,
+    KernelConfig kernel_config);
 
 std::string GetConfigFilePath(const CuttlefishConfig& config);
 std::string GetCuttlefishEnvPath();
diff --git a/host/commands/fetcher/fetch_cvd.cc b/host/commands/fetcher/fetch_cvd.cc
index 0f7fe00..b84fbba 100644
--- a/host/commands/fetcher/fetch_cvd.cc
+++ b/host/commands/fetcher/fetch_cvd.cc
@@ -424,6 +424,12 @@
       if (build_api.ArtifactToFile(kernel_build, "bzImage", local_path)) {
         AddFilesToConfig(FileSource::KERNEL_BUILD, kernel_build, {local_path},
                          &config, target_dir);
+      }
+      // If the kernel is from an arm/aarch64 build, the artifact will be called
+      // Image.
+      else if (build_api.ArtifactToFile(kernel_build, "Image", local_path)) {
+        AddFilesToConfig(FileSource::KERNEL_BUILD, kernel_build, {local_path},
+                         &config, target_dir);
       } else {
         LOG(FATAL) << "Could not download " << kernel_build << ":bzImage to "
             << local_path;
@@ -454,6 +460,13 @@
       if (build_api.ArtifactToFile(bootloader_build, "u-boot.rom", local_path)) {
         AddFilesToConfig(FileSource::BOOTLOADER_BUILD, bootloader_build,
                          {local_path}, &config, target_dir, true);
+      }
+      // If the bootloader is from an arm/aarch64 build, the artifact will be of
+      // filetype bin.
+      else if (build_api.ArtifactToFile(bootloader_build, "u-boot.bin",
+                                        local_path)) {
+        AddFilesToConfig(FileSource::BOOTLOADER_BUILD, bootloader_build,
+                         {local_path}, &config, target_dir, true);
       } else {
         LOG(FATAL) << "Could not download " << bootloader_build << ":u-boot.rom to "
             << local_path;
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index ba1323c..a2432e1 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -100,6 +100,18 @@
   }
   cmd->AddParameter("-keyboard_fd=", keyboard_server);
 
+  if (config.enable_webrtc() &&
+      config.vm_manager() == vm_manager::CrosvmManager::name()) {
+    SharedFD switches_server =
+        CreateUnixInputServer(instance.switches_socket_path());
+    if (!switches_server->IsOpen()) {
+      LOG(ERROR) << "Could not open switches server: "
+                 << switches_server->StrError();
+      return;
+    }
+    cmd->AddParameter("-switches_fd=", switches_server);
+  }
+
   SharedFD frames_server = CreateUnixInputServer(instance.frames_socket_path());
   if (!frames_server->IsOpen()) {
     LOG(ERROR) << "Could not open frames server: " << frames_server->StrError();
@@ -180,6 +192,12 @@
   command.AddParameter(instance.rootcanal_hci_port());
   // Link server port
   command.AddParameter(instance.rootcanal_link_port());
+  // Bluetooth controller properties file
+  command.AddParameter("--controller_properties_file=",
+                       instance.rootcanal_config_file());
+  // Default commands file
+  command.AddParameter("--default_commands_file=",
+                       instance.rootcanal_default_commands_file());
 
   process_monitor->AddCommand(std::move(command));
   return;
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index 8945da0..13f840b 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -224,8 +224,8 @@
 
   auto overlay_path = instance.PerInstancePath("overlay.img");
   unlink(overlay_path.c_str());
-  if (!CreateQcowOverlay(
-      config->crosvm_binary(), instance.composite_disk_path(), overlay_path)) {
+  if (!CreateQcowOverlay(config->crosvm_binary(),
+                         instance.os_composite_disk_path(), overlay_path)) {
     LOG(ERROR) << "CreateQcowOverlay failed";
     return false;
   }
@@ -437,7 +437,7 @@
     LOG(ERROR) << "Ethernet TAP device already in use";
   }
 
-  auto vm_manager = GetVmManager(config->vm_manager());
+  auto vm_manager = GetVmManager(config->vm_manager(), config->target_arch());
 
 #ifndef __ANDROID__
   // Check host configuration
diff --git a/host/commands/secure_env/keymaster_responder.cpp b/host/commands/secure_env/keymaster_responder.cpp
index 70cc9b3..013d5e7 100644
--- a/host/commands/secure_env/keymaster_responder.cpp
+++ b/host/commands/secure_env/keymaster_responder.cpp
@@ -101,8 +101,8 @@
     }
     case DESTROY_ATTESTATION_IDS:
       // Cuttlefish doesn't support ID attestation.
-      break;
+    default:
+      LOG(ERROR) << "Unknown request type: " << request->cmd;
+      return false;
   }
-  LOG(ERROR) << "Unknown request type: " << request->cmd;
-  return false;
 }
diff --git a/host/frontend/webrtc/connection_observer.cpp b/host/frontend/webrtc/connection_observer.cpp
index c769f9b..3be8fa4 100644
--- a/host/frontend/webrtc/connection_observer.cpp
+++ b/host/frontend/webrtc/connection_observer.cpp
@@ -194,6 +194,19 @@
                          buffer->size());
   }
 
+  void OnSwitchEvent(uint16_t code, bool state) override {
+    auto buffer = GetEventBuffer();
+    if (!buffer) {
+      LOG(ERROR) << "Failed to allocate event buffer";
+      return;
+    }
+    buffer->AddEvent(EV_SW, code, state);
+    buffer->AddEvent(EV_SYN, SYN_REPORT, 0);
+    cuttlefish::WriteAll(input_sockets_.switches_client,
+                         reinterpret_cast<const char *>(buffer->data()),
+                         buffer->size());
+  }
+
   void OnAdbChannelOpen(std::function<bool(const uint8_t *, size_t)>
                             adb_message_sender) override {
     LOG(VERBOSE) << "Adb Channel open";
diff --git a/host/frontend/webrtc/connection_observer.h b/host/frontend/webrtc/connection_observer.h
index 3d12807..9ec1e49 100644
--- a/host/frontend/webrtc/connection_observer.h
+++ b/host/frontend/webrtc/connection_observer.h
@@ -30,6 +30,8 @@
   cuttlefish::SharedFD touch_client;
   cuttlefish::SharedFD keyboard_server;
   cuttlefish::SharedFD keyboard_client;
+  cuttlefish::SharedFD switches_server;
+  cuttlefish::SharedFD switches_client;
 };
 
 class CfConnectionObserverFactory
diff --git a/host/frontend/webrtc/lib/connection_observer.h b/host/frontend/webrtc/lib/connection_observer.h
index 9cd7286..149ffdc 100644
--- a/host/frontend/webrtc/lib/connection_observer.h
+++ b/host/frontend/webrtc/lib/connection_observer.h
@@ -35,6 +35,7 @@
   virtual void OnMultiTouchEvent(const std::string& label, Json::Value id, Json::Value slot,
                                  Json::Value x, Json::Value y, bool down, int size) = 0;
   virtual void OnKeyboardEvent(uint16_t keycode, bool down) = 0;
+  virtual void OnSwitchEvent(uint16_t code, bool state) = 0;
   virtual void OnAdbChannelOpen(
       std::function<bool(const uint8_t*, size_t)> adb_message_sender) = 0;
   virtual void OnAdbMessage(const uint8_t* msg, size_t size) = 0;
diff --git a/host/frontend/webrtc/lib/streamer.cpp b/host/frontend/webrtc/lib/streamer.cpp
index 867dcac..31ec7fe 100644
--- a/host/frontend/webrtc/lib/streamer.cpp
+++ b/host/frontend/webrtc/lib/streamer.cpp
@@ -151,6 +151,9 @@
 std::unique_ptr<Streamer> Streamer::Create(
     const StreamerConfig& cfg,
     std::shared_ptr<ConnectionObserverFactory> connection_observer_factory) {
+
+  rtc::LogMessage::LogToDebug(rtc::LS_ERROR);
+
   std::unique_ptr<Streamer::Impl> impl(new Streamer::Impl());
   impl->config_ = cfg;
   impl->connection_observer_factory_ = connection_observer_factory;
diff --git a/host/frontend/webrtc/main.cpp b/host/frontend/webrtc/main.cpp
index 7d31411..d6021a2 100644
--- a/host/frontend/webrtc/main.cpp
+++ b/host/frontend/webrtc/main.cpp
@@ -41,6 +41,7 @@
 
 DEFINE_int32(touch_fd, -1, "An fd to listen on for touch connections.");
 DEFINE_int32(keyboard_fd, -1, "An fd to listen on for keyboard connections.");
+DEFINE_int32(switches_fd, -1, "An fd to listen on for switch connections.");
 DEFINE_int32(frame_server_fd, -1, "An fd to listen on for frame updates");
 DEFINE_int32(kernel_log_events_fd, -1,
              "An fd to listen on for kernel log events.");
@@ -129,9 +130,11 @@
 
   input_sockets.touch_server = cuttlefish::SharedFD::Dup(FLAGS_touch_fd);
   input_sockets.keyboard_server = cuttlefish::SharedFD::Dup(FLAGS_keyboard_fd);
+  input_sockets.switches_server = cuttlefish::SharedFD::Dup(FLAGS_switches_fd);
   auto control_socket = cuttlefish::SharedFD::Dup(FLAGS_command_fd);
   close(FLAGS_touch_fd);
   close(FLAGS_keyboard_fd);
+  close(FLAGS_switches_fd);
   close(FLAGS_command_fd);
   // Accepting on these sockets here means the device won't register with the
   // operator as soon as it could, but rather wait until crosvm's input display
@@ -142,6 +145,8 @@
       cuttlefish::SharedFD::Accept(*input_sockets.touch_server);
   input_sockets.keyboard_client =
       cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
+  input_sockets.switches_client =
+      cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
 
   std::thread touch_accepter([&input_sockets]() {
     for (;;) {
@@ -155,6 +160,12 @@
           cuttlefish::SharedFD::Accept(*input_sockets.keyboard_server);
     }
   });
+  std::thread switches_accepter([&input_sockets]() {
+    for (;;) {
+      input_sockets.switches_client =
+          cuttlefish::SharedFD::Accept(*input_sockets.switches_server);
+    }
+  });
 
   auto kernel_log_events_client =
       cuttlefish::SharedFD::Dup(FLAGS_kernel_log_events_fd);
diff --git a/host/frontend/webrtc_operator/assets/js/app.js b/host/frontend/webrtc_operator/assets/js/app.js
index b95bf21..5ca5fb7 100644
--- a/host/frontend/webrtc_operator/assets/js/app.js
+++ b/host/frontend/webrtc_operator/assets/js/app.js
@@ -306,7 +306,8 @@
     let dpi = display.dpi;
     let x_res = display.x_res;
     let y_res = display.y_res;
-    displayDetailsText = `Display - ${x_res}x${y_res} (${dpi}DPI)`;
+    let rotated = currentRotation == 1 ? ' (Rotated)' : '';
+    displayDetailsText = `Display - ${x_res}x${y_res} (${dpi}DPI)${rotated}`;
     updateDeviceDetailsText();
   }
 
diff --git a/host/libs/config/bootconfig_args.cpp b/host/libs/config/bootconfig_args.cpp
index b7c7f7a..c2bc1eb 100644
--- a/host/libs/config/bootconfig_args.cpp
+++ b/host/libs/config/bootconfig_args.cpp
@@ -83,9 +83,9 @@
   std::vector<std::string> bootconfig_args;
 
   AppendVector(&bootconfig_args, VmManagerBootconfig(config));
-  auto vmm = vm_manager::GetVmManager(config.vm_manager());
-  AppendVector(&bootconfig_args,
-               vmm->ConfigureBootDevices(instance.virtual_disk_paths().size()));
+  auto vmm = vm_manager::GetVmManager(config.vm_manager(), config.target_arch());
+  bootconfig_args.push_back(
+      vmm->ConfigureBootDevices(instance.virtual_disk_paths().size()));
   AppendVector(&bootconfig_args, vmm->ConfigureGpuMode(config.gpu_mode()));
 
   bootconfig_args.push_back(
@@ -155,6 +155,12 @@
 
   bootconfig_args.push_back("androidboot.verifiedbootstate=orange");
 
+  // Non-native architecture implies a significantly slower execution speed, so
+  // set a large timeout multiplier.
+  if (!IsHostCompatible(config.target_arch())) {
+    bootconfig_args.push_back("androidboot.hw_timeout_multiplier=50");
+  }
+
   // TODO(b/173815685): Create an extra_bootconfig flag and add it to bootconfig
 
   return bootconfig_args;
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index c1978bf..b9b04e5 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -259,12 +259,12 @@
   (*dictionary_)[kSetupWizardMode] = mode;
 }
 
-static constexpr char kQemuBinary[] = "qemu_binary";
-std::string CuttlefishConfig::qemu_binary() const {
-  return (*dictionary_)[kQemuBinary].asString();
+static constexpr char kQemuBinaryDir[] = "qemu_binary_dir";
+std::string CuttlefishConfig::qemu_binary_dir() const {
+  return (*dictionary_)[kQemuBinaryDir].asString();
 }
-void CuttlefishConfig::set_qemu_binary(const std::string& qemu_binary) {
-  (*dictionary_)[kQemuBinary] = qemu_binary;
+void CuttlefishConfig::set_qemu_binary_dir(const std::string& qemu_binary_dir) {
+  (*dictionary_)[kQemuBinaryDir] = qemu_binary_dir;
 }
 
 static constexpr char kCrosvmBinary[] = "crosvm_binary";
@@ -676,7 +676,8 @@
     console_dev = "hvc1";
   } else {
     // crosvm ARM does not support ttyAMA. ttyAMA is a part of ARM arch.
-    if (HostArch() == "aarch64" &&
+    Arch target = target_arch();
+    if ((target == Arch::Arm64 || target == Arch::Arm) &&
         vm_manager() != vm_manager::CrosvmManager::name()) {
       console_dev = "ttyAMA0";
     } else {
@@ -734,6 +735,22 @@
   return (*dictionary_)[kProtectedVm].asBool();
 }
 
+static constexpr char kTargetArch[] = "target_arch";
+void CuttlefishConfig::set_target_arch(Arch target_arch) {
+  (*dictionary_)[kTargetArch] = static_cast<int>(target_arch);
+}
+Arch CuttlefishConfig::target_arch() const {
+  return static_cast<Arch>((*dictionary_)[kTargetArch].asInt());
+}
+
+static constexpr char kBootconfigSupported[] = "bootconfig_supported";
+bool CuttlefishConfig::bootconfig_supported() const {
+  return (*dictionary_)[kBootconfigSupported].asBool();
+}
+void CuttlefishConfig::set_bootconfig_supported(bool bootconfig_supported) {
+  (*dictionary_)[kBootconfigSupported] = bootconfig_supported;
+}
+
 // Creates the (initially empty) config object and populates it with values from
 // the config file if the CUTTLEFISH_CONFIG_FILE env variable is present.
 // Returns nullptr if there was an error loading from file
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 9aa94b0..aaf01fb 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -25,6 +25,7 @@
 #include <set>
 #include <vector>
 
+#include "common/libs/utils/environment.h"
 #include "host/libs/config/custom_actions.h"
 
 namespace Json {
@@ -132,8 +133,8 @@
   void set_setupwizard_mode(const std::string& title);
   std::string setupwizard_mode() const;
 
-  void set_qemu_binary(const std::string& qemu_binary);
-  std::string qemu_binary() const;
+  void set_qemu_binary_dir(const std::string& qemu_binary_dir);
+  std::string qemu_binary_dir() const;
 
   void set_crosvm_binary(const std::string& crosvm_binary);
   std::string crosvm_binary() const;
@@ -308,6 +309,12 @@
   void set_protected_vm(bool protected_vm);
   bool protected_vm() const;
 
+  void set_target_arch(Arch target_arch);
+  Arch target_arch() const;
+
+  void set_bootconfig_supported(bool bootconfig_supported);
+  bool bootconfig_supported() const;
+
   class InstanceSpecific;
   class MutableInstanceSpecific;
 
@@ -363,6 +370,9 @@
     int rootcanal_hci_port() const;
     int rootcanal_link_port() const;
     int rootcanal_test_port() const;
+    std::string rootcanal_config_file() const;
+    std::string rootcanal_default_commands_file() const;
+
     std::string adb_device_name() const;
     std::string device_title() const;
     std::string gnss_file_path() const;
@@ -388,6 +398,7 @@
 
     std::string touch_socket_path() const;
     std::string keyboard_socket_path() const;
+    std::string switches_socket_path() const;
     std::string frames_socket_path() const;
 
     std::string access_kregistry_path() const;
@@ -416,7 +427,7 @@
 
     std::string sdcard_path() const;
 
-    std::string composite_disk_path() const;
+    std::string os_composite_disk_path() const;
 
     std::string uboot_env_image_path() const;
 
@@ -467,6 +478,9 @@
     void set_rootcanal_hci_port(int rootcanal_hci_port);
     void set_rootcanal_link_port(int rootcanal_link_port);
     void set_rootcanal_test_port(int rootcanal_test_port);
+    void set_rootcanal_config_file(const std::string& rootcanal_config_file);
+    void set_rootcanal_default_commands_file(
+        const std::string& rootcanal_default_commands_file);
     void set_device_title(const std::string& title);
     void set_mobile_bridge_name(const std::string& mobile_bridge_name);
     void set_mobile_tap_name(const std::string& mobile_tap_name);
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index 4dd7dbc..ee18700 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -165,8 +165,8 @@
   return AbsolutePath(PerInstancePath("sdcard.img"));
 }
 
-std::string CuttlefishConfig::InstanceSpecific::composite_disk_path() const {
-  return AbsolutePath(PerInstancePath("composite.img"));
+std::string CuttlefishConfig::InstanceSpecific::os_composite_disk_path() const {
+  return AbsolutePath(PerInstancePath("os_composite.img"));
 }
 
 std::string CuttlefishConfig::InstanceSpecific::uboot_env_image_path() const {
@@ -379,6 +379,29 @@
   (*Dictionary())[kRootcanalTestPort] = rootcanal_test_port;
 }
 
+static constexpr char kRootcanalConfigFile[] = "rootcanal_config_file";
+std::string CuttlefishConfig::InstanceSpecific::rootcanal_config_file() const {
+  return (*Dictionary())[kRootcanalConfigFile].asString();
+}
+void CuttlefishConfig::MutableInstanceSpecific::set_rootcanal_config_file(
+    const std::string& rootcanal_config_file) {
+  (*Dictionary())[kRootcanalConfigFile] =
+      DefaultHostArtifactsPath(rootcanal_config_file);
+}
+
+static constexpr char kRootcanalDefaultCommandsFile[] =
+    "rootcanal_default_commands_file";
+std::string
+CuttlefishConfig::InstanceSpecific::rootcanal_default_commands_file() const {
+  return (*Dictionary())[kRootcanalDefaultCommandsFile].asString();
+}
+void CuttlefishConfig::MutableInstanceSpecific::
+    set_rootcanal_default_commands_file(
+        const std::string& rootcanal_default_commands_file) {
+  (*Dictionary())[kRootcanalDefaultCommandsFile] =
+      DefaultHostArtifactsPath(rootcanal_default_commands_file);
+}
+
 static constexpr char kWebrtcDeviceId[] = "webrtc_device_id";
 void CuttlefishConfig::MutableInstanceSpecific::set_webrtc_device_id(
     const std::string& id) {
@@ -404,6 +427,10 @@
   return PerInstanceInternalPath("keyboard.sock");
 }
 
+std::string CuttlefishConfig::InstanceSpecific::switches_socket_path() const {
+  return PerInstanceInternalPath("switches.sock");
+}
+
 std::string CuttlefishConfig::InstanceSpecific::frames_socket_path() const {
   return PerInstanceInternalPath("frames.sock");
 }
diff --git a/host/libs/config/kernel_args.cpp b/host/libs/config/kernel_args.cpp
index 7a9a40e..421950a 100644
--- a/host/libs/config/kernel_args.cpp
+++ b/host/libs/config/kernel_args.cpp
@@ -46,7 +46,8 @@
     // crosvm sets up the console= earlycon= panic= flags for us if booting straight to
     // the kernel, but QEMU and the bootloader via crosvm does not.
     AppendVector(&vm_manager_cmdline, {"console=hvc0", "panic=-1"});
-    if (HostArch() == "aarch64") {
+    Arch target_arch = config.target_arch();
+    if (target_arch == Arch::Arm64 || target_arch == Arch::Arm) {
       if (config.vm_manager() == QemuManager::name()) {
         // To update the pl011 address:
         // $ qemu-system-aarch64 -machine virt -cpu cortex-a57 -machine dumpdtb=virt.dtb
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 58ae199..e2ec717 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -110,7 +110,7 @@
         "androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_1),
         "androidboot.hardware.gralloc=minigbm",
         "androidboot.hardware.hwcomposer=ranchu",
-        "androidboot.hardware.egl=swiftshader",
+        "androidboot.hardware.egl=angle",
         "androidboot.hardware.vulkan=pastel",
     };
   }
@@ -136,19 +136,18 @@
   return {};
 }
 
-std::vector<std::string> CrosvmManager::ConfigureBootDevices(int num_disks) {
+std::string CrosvmManager::ConfigureBootDevices(int num_disks) {
   // TODO There is no way to control this assignment with crosvm (yet)
-  if (HostArch() == "x86_64") {
+  if (HostArch() == Arch::X86_64) {
     // crosvm has an additional PCI device for an ISA bridge
     std::stringstream stream;
     stream << std::setfill('0') << std::setw(2) << std::hex
            << 1 + VmManager::kDefaultNumHvcs + VmManager::kMaxDisks - num_disks;
-    return {"androidboot.boot_devices=pci0000:00/0000:00:" + stream.str() +
-            ".0"};
+    return "androidboot.boot_devices=pci0000:00/0000:00:" + stream.str() + ".0";
   } else {
     // On ARM64 crosvm, block devices are on their own bridge, so we don't
     // need to calculate it, and the path is always the same
-    return { "androidboot.boot_devices=10000.pci" };
+    return "androidboot.boot_devices=10000.pci";
   }
 }
 
@@ -266,6 +265,9 @@
                             display_config.height);
     crosvm_cmd.AddParameter("--keyboard=", instance.keyboard_socket_path());
   }
+  if (config.enable_webrtc()) {
+    crosvm_cmd.AddParameter("--switches=", instance.switches_socket_path());
+  }
 
   auto wifi_tap = AddTapFdParameter(&crosvm_cmd, instance.wifi_tap_name());
   AddTapFdParameter(&crosvm_cmd, instance.mobile_tap_name());
diff --git a/host/libs/vm_manager/crosvm_manager.h b/host/libs/vm_manager/crosvm_manager.h
index d6e0e01..9d41d68 100644
--- a/host/libs/vm_manager/crosvm_manager.h
+++ b/host/libs/vm_manager/crosvm_manager.h
@@ -31,12 +31,12 @@
 class CrosvmManager : public VmManager {
  public:
   static std::string name() { return "crosvm"; }
-  CrosvmManager() = default;
+  CrosvmManager(Arch arch) : VmManager(arch) {}
   virtual ~CrosvmManager() = default;
 
   bool IsSupported() override;
   std::vector<std::string> ConfigureGpuMode(const std::string&) override;
-  std::vector<std::string> ConfigureBootDevices(int num_disks) override;
+  std::string ConfigureBootDevices(int num_disks) override;
 
   std::vector<cuttlefish::Command> StartCommands(
       const CuttlefishConfig& config) override;
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 72989f6..ed61c69 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -120,12 +120,23 @@
   return {};
 }
 
-std::vector<std::string> QemuManager::ConfigureBootDevices(int num_disks) {
-  // QEMU has additional PCI devices for an ISA bridge and PIIX4
-  std::stringstream stream;
-  stream << std::setfill('0') << std::setw(2) << std::hex
-         << 2 + VmManager::kDefaultNumHvcs + VmManager::kMaxDisks - num_disks;
-  return {"androidboot.boot_devices=pci0000:00/0000:00:" + stream.str() + ".0"};
+std::string QemuManager::ConfigureBootDevices(int num_disks) {
+  switch (arch_) {
+    case Arch::X86:
+    case Arch::X86_64: {
+      // QEMU has additional PCI devices for an ISA bridge and PIIX4
+      std::stringstream stream;
+      stream << std::setfill('0') << std::setw(2) << std::hex
+             << 2 + VmManager::kDefaultNumHvcs + VmManager::kMaxDisks -
+                    num_disks;
+      return "androidboot.boot_devices=pci0000:00/0000:00:" + stream.str() +
+             ".0";
+    }
+    case Arch::Arm:
+      return "androidboot.boot_devices=3f000000.pcie";
+    case Arch::Arm64:
+      return "androidboot.boot_devices=4010000000.pcie";
+  }
 }
 
 std::vector<Command> QemuManager::StartCommands(
@@ -141,7 +152,22 @@
                   << "attempting to KILL";
     return KillSubprocess(proc);
   };
-  Command qemu_cmd(config.qemu_binary(), stop);
+  std::string qemu_binary = config.qemu_binary_dir();
+  switch (arch_) {
+    case Arch::Arm:
+      qemu_binary += "/qemu-system-arm";
+      break;
+    case Arch::Arm64:
+      qemu_binary += "/qemu-system-aarch64";
+      break;
+    case Arch::X86:
+      qemu_binary += "/qemu-system-i386";
+      break;
+    case Arch::X86_64:
+      qemu_binary += "/qemu-system-x86_64";
+      break;
+  }
+  Command qemu_cmd(qemu_binary, stop);
 
   int hvc_num = 0;
   int serial_num = 0;
@@ -207,7 +233,7 @@
     hvc_num++;
   };
 
-  bool is_arm = android::base::EndsWith(config.qemu_binary(), "system-aarch64");
+  bool is_arm = arch_ == Arch::Arm || arch_ == Arch::Arm64;
 
   auto access_kregistry_size_bytes = 0;
   if (FileExists(instance.access_kregistry_path())) {
@@ -229,7 +255,8 @@
   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,nvdimm=on";
+  auto machine = is_arm ? "virt,gic-version=2,mte=on"
+                        : "pc-i440fx-2.8,accel=kvm,nvdimm=on";
   qemu_cmd.AddParameter(machine, ",usb=off,dump-guest-core=off");
 
   qemu_cmd.AddParameter("-m");
@@ -428,7 +455,7 @@
   qemu_cmd.AddParameter("virtio-gpu-pci,id=gpu0");
 
   qemu_cmd.AddParameter("-cpu");
-  qemu_cmd.AddParameter(is_arm ? "cortex-a53" : "host");
+  qemu_cmd.AddParameter(IsHostCompatible(arch_) ? "host" : "max");
 
   qemu_cmd.AddParameter("-msg");
   qemu_cmd.AddParameter("timestamp=on");
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
index 3535246..934a976 100644
--- a/host/libs/vm_manager/qemu_manager.h
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -31,12 +31,12 @@
  public:
   static std::string name() { return "qemu_cli"; }
 
-  QemuManager() = default;
+  QemuManager(Arch arch) : VmManager(arch) {}
   virtual ~QemuManager() = default;
 
   bool IsSupported() override;
   std::vector<std::string> ConfigureGpuMode(const std::string&) override;
-  std::vector<std::string> ConfigureBootDevices(int num_disks) override;
+  std::string ConfigureBootDevices(int num_disks) override;
 
   std::vector<cuttlefish::Command> StartCommands(
       const CuttlefishConfig& config) override;
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index 30aeee7..3a5a41d 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -27,12 +27,12 @@
 namespace cuttlefish {
 namespace vm_manager {
 
-std::unique_ptr<VmManager> GetVmManager(const std::string& name) {
+std::unique_ptr<VmManager> GetVmManager(const std::string& name, Arch arch) {
   std::unique_ptr<VmManager> vmm;
   if (name == QemuManager::name()) {
-    vmm.reset(new QemuManager());
+    vmm.reset(new QemuManager(arch));
   } else if (name == CrosvmManager::name()) {
-    vmm.reset(new CrosvmManager());
+    vmm.reset(new CrosvmManager(arch));
   }
   if (!vmm) {
     LOG(ERROR) << "Invalid VM manager: " << name;
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index b482b2c..2af2846 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -26,6 +26,9 @@
 
 // Superclass of every guest VM manager.
 class VmManager {
+ protected:
+  const Arch arch_;
+
  public:
   // This is the number of HVC virtual console ports that should be configured
   // by the VmManager. Because crosvm currently allocates these ports as the
@@ -48,11 +51,12 @@
   // assigned virtual disk PCI ID (i.e. 2 disks = 7 hvcs, 1 disks = 8 hvcs)
   static const int kMaxDisks = 3;
 
+  VmManager(Arch arch) : arch_(arch) {}
   virtual ~VmManager() = default;
 
   virtual bool IsSupported() = 0;
   virtual std::vector<std::string> ConfigureGpuMode(const std::string&) = 0;
-  virtual std::vector<std::string> ConfigureBootDevices(int num_disks) = 0;
+  virtual std::string ConfigureBootDevices(int num_disks) = 0;
 
   // Starts the VMM. It will usually build a command and pass it to the
   // command_starter function, although it may start more than one. The
@@ -62,7 +66,7 @@
       const CuttlefishConfig& config) = 0;
 };
 
-std::unique_ptr<VmManager> GetVmManager(const std::string&);
+std::unique_ptr<VmManager> GetVmManager(const std::string&, Arch arch);
 
 } // namespace vm_manager
 } // namespace cuttlefish
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 27114bf..87c62b3 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -123,10 +123,6 @@
 BOARD_USERDATAIMAGE_FILE_SYSTEM_TYPE := $(TARGET_USERDATAIMAGE_FILE_SYSTEM_TYPE)
 TARGET_USERIMAGES_USE_F2FS := true
 
-# Cache partition size: 64M
-BOARD_CACHEIMAGE_PARTITION_SIZE := 67108864
-BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE := ext4
-
 BOARD_GPU_DRIVERS := virgl
 
 # Enable goldfish's encoder.
@@ -182,12 +178,13 @@
 TARGET_RECOVERY_UI_LIB := librecovery_ui_cuttlefish
 TARGET_RECOVERY_FSTAB ?= device/google/cuttlefish/shared/config/fstab.f2fs
 
-BOARD_SUPER_PARTITION_SIZE := 6442450944
+BOARD_SUPER_PARTITION_SIZE := 7516192768  # 7GiB
 BOARD_SUPER_PARTITION_GROUPS := google_system_dynamic_partitions google_vendor_dynamic_partitions
 BOARD_GOOGLE_SYSTEM_DYNAMIC_PARTITIONS_PARTITION_LIST := product system system_ext
-BOARD_GOOGLE_SYSTEM_DYNAMIC_PARTITIONS_SIZE := 5368709120  # 5GiB
+BOARD_GOOGLE_SYSTEM_DYNAMIC_PARTITIONS_SIZE := 5637144576  # 5.25GiB
 BOARD_GOOGLE_VENDOR_DYNAMIC_PARTITIONS_PARTITION_LIST := odm vendor vendor_dlkm odm_dlkm
-BOARD_GOOGLE_VENDOR_DYNAMIC_PARTITIONS_SIZE := 1073741824
+# 1532MiB, reserve 4MiB for dynamic partition metadata
+BOARD_GOOGLE_VENDOR_DYNAMIC_PARTITIONS_SIZE := 1606418432
 BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT := true
 BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE := true
 TARGET_RELEASETOOLS_EXTENSIONS := device/google/cuttlefish/shared
diff --git a/shared/auto/device.mk b/shared/auto/device.mk
index 3f30202..ada04d4 100644
--- a/shared/auto/device.mk
+++ b/shared/auto/device.mk
@@ -38,6 +38,7 @@
 endif
 
 PRODUCT_COPY_FILES += \
+    frameworks/native/data/etc/android.hardware.bluetooth.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.bluetooth.xml \
     frameworks/native/data/etc/android.hardware.broadcastradio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.broadcastradio.xml \
     frameworks/native/data/etc/android.hardware.faketouch.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.faketouch.xml \
     frameworks/native/data/etc/android.hardware.screen.landscape.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.screen.landscape.xml \
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 3cd5a02..184833f 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -9,6 +9,7 @@
     setprop ro.hardware.hwcomposer ${ro.boot.hardware.hwcomposer}
     setprop ro.hardware.vulkan ${ro.boot.hardware.vulkan}
     setprop ro.cpuvulkan.version ${ro.boot.cpuvulkan.version}
+    setprop ro.hw_timeout_multiplier ${ro.boot.hw_timeout_multiplier}
 
     # start module load in the background
     start vendor.insmod_sh
diff --git a/shared/config/manifest.xml b/shared/config/manifest.xml
index bf3896b..b14b82c 100644
--- a/shared/config/manifest.xml
+++ b/shared/config/manifest.xml
@@ -67,15 +67,6 @@
     </hal>
     -->
     <hal format="hidl">
-        <name>android.hardware.bluetooth</name>
-        <transport>hwbinder</transport>
-        <version>1.1</version>
-        <interface>
-            <name>IBluetoothHci</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl">
         <name>android.hardware.bluetooth.audio</name>
         <transport>hwbinder</transport>
         <version>2.1</version>
diff --git a/shared/config/manifest_android.hardware.bluetooth@1.1-service.xml b/shared/config/manifest_android.hardware.bluetooth@1.1-service.xml
new file mode 100644
index 0000000..4d70779
--- /dev/null
+++ b/shared/config/manifest_android.hardware.bluetooth@1.1-service.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2021, 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.
+*/
+-->
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.bluetooth</name>
+        <transport>hwbinder</transport>
+        <version>1.1</version>
+        <interface>
+            <name>IBluetoothHci</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/shared/device.mk b/shared/device.mk
index 552426f..3140a21 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -367,16 +367,21 @@
 #
 # Bluetooth HAL and Compatibility Bluetooth library (for older revs).
 #
-
+ifeq ($(LOCAL_BLUETOOTH_PRODUCT_PACKAGE),)
 ifeq ($(TARGET_ENABLE_HOST_BLUETOOTH_EMULATION),true)
 ifeq ($(TARGET_USE_BTLINUX_HAL_IMPL),true)
-    PRODUCT_PACKAGES += android.hardware.bluetooth@1.1-service.btlinux
+    LOCAL_BLUETOOTH_PRODUCT_PACKAGE := android.hardware.bluetooth@1.1-service.btlinux
 else
-    PRODUCT_PACKAGES += android.hardware.bluetooth@1.1-service.remote
+    LOCAL_BLUETOOTH_PRODUCT_PACKAGE := android.hardware.bluetooth@1.1-service.remote
 endif
 else
-    PRODUCT_PACKAGES += android.hardware.bluetooth@1.1-service.sim
+    LOCAL_BLUETOOTH_PRODUCT_PACKAGE := android.hardware.bluetooth@1.1-service.sim
 endif
+    DEVICE_MANIFEST_FILE += device/google/cuttlefish/shared/config/manifest_android.hardware.bluetooth@1.1-service.xml
+endif
+
+PRODUCT_PACKAGES += $(LOCAL_BLUETOOTH_PRODUCT_PACKAGE)
+
 PRODUCT_PACKAGES += android.hardware.bluetooth.audio@2.1-impl
 
 #
@@ -445,7 +450,7 @@
 # Gatekeeper
 #
 ifeq ($(LOCAL_GATEKEEPER_PRODUCT_PACKAGE),)
-       LOCAL_GATEKEEPER_PRODUCT_PACKAGE := android.hardware.gatekeeper@1.0-service.remote
+       LOCAL_GATEKEEPER_PRODUCT_PACKAGE := android.hardware.gatekeeper@1.0-service.software
 endif
 PRODUCT_PACKAGES += \
     $(LOCAL_GATEKEEPER_PRODUCT_PACKAGE)
@@ -500,7 +505,7 @@
 # Keymaster HAL
 #
 ifeq ($(LOCAL_KEYMASTER_PRODUCT_PACKAGE),)
-       LOCAL_KEYMASTER_PRODUCT_PACKAGE := android.hardware.keymaster@4.1-service.remote
+       LOCAL_KEYMASTER_PRODUCT_PACKAGE := android.hardware.keymaster@4.1-service
 endif
 PRODUCT_PACKAGES += \
     $(LOCAL_KEYMASTER_PRODUCT_PACKAGE)
@@ -539,7 +544,8 @@
     android.hardware.neuralnetworks-service-sample-float-fast \
     android.hardware.neuralnetworks-service-sample-float-slow \
     android.hardware.neuralnetworks-service-sample-minimal \
-    android.hardware.neuralnetworks-service-sample-quant
+    android.hardware.neuralnetworks-service-sample-quant \
+    android.hardware.neuralnetworks-shim-service-sample
 
 #
 # USB
diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts
index 8a5004a..7ae631d 100644
--- a/shared/sepolicy/vendor/file_contexts
+++ b/shared/sepolicy/vendor/file_contexts
@@ -76,6 +76,7 @@
 /vendor/bin/hw/android\.hardware\.health\.storage-service\.cuttlefish u:object_r:hal_health_storage_default_exec:s0
 /vendor/bin/hw/android\.hardware\.lights-service\.example u:object_r:hal_light_default_exec:s0
 /vendor/bin/hw/android\.hardware\.neuralnetworks@1\.3-service-sample-.*   u:object_r:hal_neuralnetworks_sample_exec:s0
+/vendor/bin/hw/android\.hardware\.neuralnetworks-shim-service-sample   u:object_r:hal_neuralnetworks_sample_exec:s0
 /vendor/bin/hw/android\.hardware\.neuralnetworks-service-sample-.*   u:object_r:hal_neuralnetworks_sample_exec:s0
 /vendor/bin/hw/android\.hardware\.vibrator@1\.x-service\.example u:object_r:hal_vibrator_default_exec:s0
 /vendor/bin/setup_wifi  u:object_r:setup_wifi_exec:s0
diff --git a/shared/sepolicy/vendor/service_contexts b/shared/sepolicy/vendor/service_contexts
index 7b8a515..d20d026 100644
--- a/shared/sepolicy/vendor/service_contexts
+++ b/shared/sepolicy/vendor/service_contexts
@@ -3,6 +3,7 @@
 android.hardware.neuralnetworks.IDevice/nnapi-sample_float_slow u:object_r:hal_neuralnetworks_service:s0
 android.hardware.neuralnetworks.IDevice/nnapi-sample_minimal    u:object_r:hal_neuralnetworks_service:s0
 android.hardware.neuralnetworks.IDevice/nnapi-sample_quant    u:object_r:hal_neuralnetworks_service:s0
+android.hardware.neuralnetworks.IDevice/nnapi-sample_sl_shim  u:object_r:hal_neuralnetworks_service:s0
 
 # Binder service mappings
 gce                                       u:object_r:gce_service:s0
diff --git a/shared/sepolicy/vendor/socket_vsock_proxy.te b/shared/sepolicy/vendor/socket_vsock_proxy.te
index eaca909..d4e2b1a 100644
--- a/shared/sepolicy/vendor/socket_vsock_proxy.te
+++ b/shared/sepolicy/vendor/socket_vsock_proxy.te
@@ -4,7 +4,7 @@
 init_daemon_domain(socket_vsock_proxy)
 
 allow socket_vsock_proxy self:global_capability_class_set { net_admin net_raw };
-allow socket_vsock_proxy self:{ socket vsock_socket } { create read write listen accept bind };
+allow socket_vsock_proxy self:{ socket vsock_socket } { create getopt read write listen accept bind shutdown };
 
 # TODO: socket returned by accept() has unlabeled context on it. Give it a
 # specific label.
diff --git a/tools/upload_via_ssh.py b/tools/upload_via_ssh.py
index a31aaf9..5359473 100755
--- a/tools/upload_via_ssh.py
+++ b/tools/upload_via_ssh.py
@@ -12,7 +12,7 @@
   dir = os.getcwd()
   try:
     os.chdir(args.image_dir)
-    images = glob.glob('*.img')
+    images = glob.glob('*.img') + ["bootloader"]
     if len(images) == 0:
       raise OSError('File not found: ' + args.image_dir + '/*.img')
     subprocess.check_call(