Merge "Snap for 6533464 from 31deac96f4273a3805c300566ae262ce8b2881da to sdk-release" into sdk-release
diff --git a/.clang-format b/.clang-format
index 4a3bd2f..6fa717c 100644
--- a/.clang-format
+++ b/.clang-format
@@ -36,6 +36,7 @@
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
+IncludeBlocks: Preserve
 IndentWidth: 2
 PointerAlignment: Left
 TabWidth: 2
diff --git a/Android.mk b/Android.mk
index d727ca2..5816749 100644
--- a/Android.mk
+++ b/Android.mk
@@ -18,9 +18,9 @@
 RECOVERY_API_VERSION := 3
 RECOVERY_FSTAB_VERSION := 2
 
-# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr} or a device-specific
-# module that defines make_device() and the exact RecoveryUI class for the target. It defaults to
-# librecovery_ui_default, which uses ScreenRecoveryUI.
+# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr,ethernet} or a
+# device-specific module that defines make_device() and the exact RecoveryUI class for the
+# target. It defaults to librecovery_ui_default, which uses ScreenRecoveryUI.
 TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
 
 # librecovery_ui_ext (shared library)
diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h
index d3d26da..bdd6aec 100644
--- a/edify/include/edify/updater_runtime_interface.h
+++ b/edify/include/edify/updater_runtime_interface.h
@@ -71,4 +71,7 @@
   virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0;
   virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0;
   virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0;
+
+  // On devices supports A/B, add current slot suffix to arg. Otherwise, return |arg| as is.
+  virtual std::string AddSlotSuffix(const std::string_view arg) const = 0;
 };
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2023349..a093008 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -52,6 +52,7 @@
   ui->ResetKeyInterruptStatus();
   ui->SetTitle(title_lines);
   ui->ShowText(true);
+  device->StartFastboot();
 
   // Reset to normal system boot so recovery won't cycle indefinitely.
   // TODO(b/112277594) Clear only if 'recovery' field of BCB is empty. If not,
diff --git a/install/install.cpp b/install/install.cpp
index 4bb0903..56c1e68 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -330,15 +330,25 @@
     return INSTALL_CORRUPT;
   }
 
-  bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
-  if (is_ab) {
+  bool package_is_ab = get_value(metadata, "ota-type") == OtaTypeToString(OtaType::AB);
+  bool device_supports_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
+  bool ab_device_supports_nonab =
+      android::base::GetBoolProperty("ro.virtual_ab.allow_non_ab", false);
+  bool device_only_supports_ab = device_supports_ab && !ab_device_supports_nonab;
+
+  if (package_is_ab) {
     CHECK(package->GetType() == PackageType::kFile);
   }
 
-  // Verify against the metadata in the package first.
-  if (is_ab && !CheckPackageMetadata(metadata, OtaType::AB)) {
-    log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
-    return INSTALL_ERROR;
+  // Verify against the metadata in the package first. Expects A/B metadata if:
+  // Package declares itself as an A/B package
+  // Package does not declare itself as an A/B package, but device only supports A/B;
+  //   still calls CheckPackageMetadata to get a meaningful error message.
+  if (package_is_ab || device_only_supports_ab) {
+    if (!CheckPackageMetadata(metadata, OtaType::AB)) {
+      log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
+      return INSTALL_ERROR;
+    }
   }
 
   ReadSourceTargetBuild(metadata, log_buffer);
@@ -388,8 +398,9 @@
 
   std::vector<std::string> args;
   if (auto setup_result =
-          is_ab ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args)
-                : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args);
+          package_is_ab
+              ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args)
+              : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args);
       !setup_result) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
     return INSTALL_CORRUPT;
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 30a1fc0..80cba61 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -471,6 +471,11 @@
     std::string usb_config =
         fastboot ? "fastboot" : IsRoDebuggable() || IsDeviceUnlocked() ? "adb" : "none";
     std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
+    if (fastboot) {
+      device->PreFastboot();
+    } else {
+      device->PreRecovery();
+    }
     if (usb_config != usb_state) {
       if (!SetUsbConfig("none")) {
         LOG(ERROR) << "Failed to clear USB config";
diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp
index 149ef8a..9dfee5f 100644
--- a/recovery_ui/Android.bp
+++ b/recovery_ui/Android.bp
@@ -22,6 +22,7 @@
 
     srcs: [
         "device.cpp",
+        "ethernet_ui.cpp",
         "screen_ui.cpp",
         "stub_ui.cpp",
         "ui.cpp",
@@ -90,3 +91,23 @@
 
     export_include_dirs: ["include"],
 }
+
+// The default device that uses EthernetRecoveryUI.
+cc_library_static {
+    name: "librecovery_ui_ethernet",
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "ethernet_device.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/recovery_ui/ethernet_device.cpp b/recovery_ui/ethernet_device.cpp
new file mode 100644
index 0000000..39ec65d
--- /dev/null
+++ b/recovery_ui/ethernet_device.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <linux/if.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "recovery_ui/device.h"
+#include "recovery_ui/ethernet_ui.h"
+
+class EthernetDevice : public Device {
+ public:
+  explicit EthernetDevice(EthernetRecoveryUI* ui);
+
+  void PreRecovery() override;
+  void PreFastboot() override;
+
+ private:
+  int SetInterfaceFlags(const unsigned set, const unsigned clr);
+  void SetTitleIPv6LinkLocalAddress(const bool interface_up);
+
+  android::base::unique_fd ctl_sock_;
+  static const std::string interface;
+};
+
+const std::string EthernetDevice::interface = "eth0";
+
+EthernetDevice::EthernetDevice(EthernetRecoveryUI* ui)
+    : Device(ui), ctl_sock_(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) {
+  if (ctl_sock_ < 0) {
+    PLOG(ERROR) << "Failed to open socket";
+  }
+}
+
+void EthernetDevice::PreRecovery() {
+  SetInterfaceFlags(0, IFF_UP);
+  SetTitleIPv6LinkLocalAddress(false);
+}
+
+void EthernetDevice::PreFastboot() {
+  android::base::SetProperty("fastbootd.protocol", "tcp");
+
+  if (SetInterfaceFlags(IFF_UP, 0) < 0) {
+    LOG(ERROR) << "Failed to bring up interface";
+    return;
+  }
+
+  SetTitleIPv6LinkLocalAddress(true);
+}
+
+int EthernetDevice::SetInterfaceFlags(const unsigned set, const unsigned clr) {
+  struct ifreq ifr;
+
+  if (ctl_sock_ < 0) {
+    return -1;
+  }
+
+  memset(&ifr, 0, sizeof(struct ifreq));
+  strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ - 1] = 0;
+
+  if (ioctl(ctl_sock_, SIOCGIFFLAGS, &ifr) < 0) {
+    PLOG(ERROR) << "Failed to get interface active flags";
+    return -1;
+  }
+  ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
+
+  if (ioctl(ctl_sock_, SIOCSIFFLAGS, &ifr) < 0) {
+    PLOG(ERROR) << "Failed to set interface active flags";
+    return -1;
+  }
+
+  return 0;
+}
+
+void EthernetDevice::SetTitleIPv6LinkLocalAddress(const bool interface_up) {
+  auto recovery_ui = reinterpret_cast<EthernetRecoveryUI*>(GetUI());
+  if (!interface_up) {
+    recovery_ui->SetIPv6LinkLocalAddress();
+    return;
+  }
+
+  struct ifaddrs* ifaddr;
+  if (getifaddrs(&ifaddr) == -1) {
+    PLOG(ERROR) << "Failed to get interface addresses";
+    recovery_ui->SetIPv6LinkLocalAddress();
+    return;
+  }
+
+  std::unique_ptr<struct ifaddrs, decltype(&freeifaddrs)> guard{ ifaddr, freeifaddrs };
+  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
+    if (ifa->ifa_addr->sa_family != AF_INET6 || interface != ifa->ifa_name) {
+      continue;
+    }
+
+    auto current_addr = reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr);
+    if (!IN6_IS_ADDR_LINKLOCAL(&(current_addr->sin6_addr))) {
+      continue;
+    }
+
+    char addrstr[INET6_ADDRSTRLEN];
+    inet_ntop(AF_INET6, reinterpret_cast<const void*>(&current_addr->sin6_addr), addrstr,
+              INET6_ADDRSTRLEN);
+    LOG(INFO) << "Our IPv6 link-local address is " << addrstr;
+    recovery_ui->SetIPv6LinkLocalAddress(addrstr);
+    return;
+  }
+
+  recovery_ui->SetIPv6LinkLocalAddress();
+}
+
+// -----------------------------------------------------------------------------------------
+Device* make_device() {
+  return new EthernetDevice(new EthernetRecoveryUI);
+}
diff --git a/recovery_ui/ethernet_ui.cpp b/recovery_ui/ethernet_ui.cpp
new file mode 100644
index 0000000..535d407
--- /dev/null
+++ b/recovery_ui/ethernet_ui.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include "recovery_ui/ethernet_ui.h"
+
+#include <android-base/logging.h>
+
+void EthernetRecoveryUI::SetTitle(const std::vector<std::string>& lines) {
+  ScreenRecoveryUI::SetTitle(lines);
+
+  // Append IP address, if any
+  if (!address_.empty()) {
+    title_lines_.push_back("IPv6 link-local address - " + address_);
+  }
+}
+
+void EthernetRecoveryUI::SetIPv6LinkLocalAddress(const std::string& address) {
+  address_ = address;
+}
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index f4f9936..76166f0 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -79,10 +79,22 @@
     ui_.reset(ui);
   }
 
+  // Called before recovery mode started up, to perform whatever device-specific recovery mode
+  // preparation as needed.
+  virtual void PreRecovery() {}
+
   // Called when recovery starts up (after the UI has been obtained and initialized and after the
   // arguments have been parsed, but before anything else).
   virtual void StartRecovery() {}
 
+  // Called before fastboot mode is started up, to perform whatever device-specific fastboot mode
+  // preparation as needed.
+  virtual void PreFastboot() {}
+
+  // Called when fastboot starts up (after the UI has been obtained and initialized and after the
+  // arguments have been parsed, but before anything else).
+  virtual void StartFastboot() {}
+
   // Called from the main thread when recovery is at the main menu and waiting for input, and a key
   // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible;
   // recovery will be at the main menu with it invisible after an unsuccessful operation, such as
diff --git a/recovery_ui/include/recovery_ui/ethernet_ui.h b/recovery_ui/include/recovery_ui/ethernet_ui.h
new file mode 100644
index 0000000..f40c73f
--- /dev/null
+++ b/recovery_ui/include/recovery_ui/ethernet_ui.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef RECOVERY_ETHERNET_UI_H
+#define RECOVERY_ETHERNET_UI_H
+
+#include "screen_ui.h"
+
+class EthernetRecoveryUI : public ScreenRecoveryUI {
+ public:
+  EthernetRecoveryUI() {}
+  void SetTitle(const std::vector<std::string>& lines) override;
+
+  // For EthernetDevice
+  void SetIPv6LinkLocalAddress(const std::string& address = "");
+
+ private:
+  std::string address_;
+};
+
+#endif  // RECOVERY_ETHERNET_UI_H
diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h
index 08ec1d7..512732f 100644
--- a/recovery_ui/include/recovery_ui/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -192,6 +192,8 @@
     return key_interrupted_;
   }
 
+  virtual bool IsUsbConnected();
+
  protected:
   void EnqueueKey(int key_code);
 
@@ -226,8 +228,6 @@
   void ProcessKey(int key_code, int updown);
   void TimeKey(int key_code, int count);
 
-  bool IsUsbConnected();
-
   bool InitScreensaver();
   void SetScreensaverState(ScreensaverState state);
 
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index 6dcb161..b2c828f 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -37,6 +37,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -881,10 +882,28 @@
   return true;
 }
 
+static bool InitGraphics() {
+  // Timeout is same as init wait for file default of 5 seconds and is arbitrary
+  const unsigned timeout = 500;  // 10ms increments
+  for (auto retry = timeout; retry > 0; --retry) {
+    if (gr_init() == 0) {
+      if (retry < timeout) {
+        // Log message like init wait for file completion log for consistency.
+        LOG(WARNING) << "wait for 'graphics' took " << ((timeout - retry) * 10) << "ms";
+      }
+      return true;
+    }
+    std::this_thread::sleep_for(10ms);
+  }
+  // Log message like init wait for file timeout log for consistency.
+  LOG(ERROR) << "timeout wait for 'graphics' took " << (timeout * 10) << "ms";
+  return false;
+}
+
 bool ScreenRecoveryUI::Init(const std::string& locale) {
   RecoveryUI::Init(locale);
 
-  if (gr_init() == -1) {
+  if (!InitGraphics()) {
     return false;
   }
 
diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h
index 9f7847b..fa878db 100644
--- a/updater/include/updater/simulator_runtime.h
+++ b/updater/include/updater/simulator_runtime.h
@@ -53,6 +53,7 @@
   bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
   bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
   bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
+  std::string AddSlotSuffix(const std::string_view arg) const override;
 
  private:
   std::string FindBlockDeviceName(const std::string_view name) const override;
diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h
index 8fc066f..b943dfc 100644
--- a/updater/include/updater/updater_runtime.h
+++ b/updater/include/updater/updater_runtime.h
@@ -56,6 +56,7 @@
   bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
   bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
   bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
+  std::string AddSlotSuffix(const std::string_view arg) const override;
 
  private:
   struct selabel_handle* sehandle_{ nullptr };
diff --git a/updater/install.cpp b/updater/install.cpp
index 7608dc3..afa5195 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -852,6 +852,20 @@
   return StringValue("t");
 }
 
+Value* AddSlotSuffixFn(const char* name, State* state,
+                       const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
+  }
+  std::vector<std::string> args;
+  if (!ReadArgs(state, argv, &args)) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+  }
+  const std::string& arg = args[0];
+  auto updater_runtime = state->updater->GetRuntime();
+  return StringValue(updater_runtime->AddSlotSuffix(arg));
+}
+
 void RegisterInstallFunctions() {
   RegisterFunction("mount", MountFn);
   RegisterFunction("is_mounted", IsMountedFn);
@@ -885,4 +899,6 @@
 
   RegisterFunction("enable_reboot", EnableRebootFn);
   RegisterFunction("tune2fs", Tune2FsFn);
+
+  RegisterFunction("add_slot_suffix", AddSlotSuffixFn);
 }
diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp
index 3ed7bf3..57dfb32 100644
--- a/updater/simulator_runtime.cpp
+++ b/updater/simulator_runtime.cpp
@@ -130,3 +130,8 @@
   }
   return true;
 }
+
+std::string SimulatorRuntime::AddSlotSuffix(const std::string_view arg) const {
+  LOG(INFO) << "Skip adding slot suffix to " << arg;
+  return std::string(arg);
+}
diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp
index b1b8863..e938305 100644
--- a/updater/updater_runtime.cpp
+++ b/updater/updater_runtime.cpp
@@ -28,6 +28,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/wipe.h>
+#include <fs_mgr.h>
 #include <selinux/label.h>
 #include <tune2fs.h>
 
@@ -186,3 +187,7 @@
   // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success.
   return tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data());
 }
+
+std::string UpdaterRuntime::AddSlotSuffix(const std::string_view arg) const {
+  return std::string(arg) + fs_mgr_get_slot_suffix();
+}
diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp
index be9250a..6570cff 100644
--- a/updater/updater_runtime_dynamic_partitions.cpp
+++ b/updater/updater_runtime_dynamic_partitions.cpp
@@ -41,6 +41,7 @@
 using android::fs_mgr::MetadataBuilder;
 using android::fs_mgr::Partition;
 using android::fs_mgr::PartitionOpener;
+using android::fs_mgr::SlotNumberForSlotSuffix;
 
 static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
 
@@ -48,13 +49,17 @@
   return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
 }
 
-static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
-  auto state = DeviceMapper::Instance().GetState(partition_name);
+static std::string AddSlotSuffix(const std::string& partition_name) {
+  return partition_name + fs_mgr_get_slot_suffix();
+}
+
+static bool UnmapPartitionWithSuffixOnDeviceMapper(const std::string& partition_name_suffix) {
+  auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
   if (state == DmDeviceState::INVALID) {
     return true;
   }
   if (state == DmDeviceState::ACTIVE) {
-    return DestroyLogicalPartition(partition_name);
+    return DestroyLogicalPartition(partition_name_suffix);
   }
   LOG(ERROR) << "Unknown device mapper state: "
              << static_cast<std::underlying_type_t<DmDeviceState>>(state);
@@ -63,12 +68,17 @@
 
 bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
                                                 std::string* path) {
-  auto state = DeviceMapper::Instance().GetState(partition_name);
+  auto partition_name_suffix = AddSlotSuffix(partition_name);
+  auto state = DeviceMapper::Instance().GetState(partition_name_suffix);
   if (state == DmDeviceState::INVALID) {
     CreateLogicalPartitionParams params = {
       .block_device = GetSuperDevice(),
-      .metadata_slot = 0,
-      .partition_name = partition_name,
+      // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
+      // SlotNumberForSlotSuffix("") returns 0.
+      .metadata_slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix()),
+      // If device supports A/B, apply non-A/B update to the partition at current slot. Otherwise,
+      // fs_mgr_get_slot_suffix() returns empty string.
+      .partition_name = partition_name_suffix,
       .force_writable = true,
       .timeout_ms = kMapTimeout,
     };
@@ -76,7 +86,7 @@
   }
 
   if (state == DmDeviceState::ACTIVE) {
-    return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
+    return DeviceMapper::Instance().GetDmDevicePathByName(partition_name_suffix, path);
   }
   LOG(ERROR) << "Unknown device mapper state: "
              << static_cast<std::underlying_type_t<DmDeviceState>>(state);
@@ -84,7 +94,7 @@
 }
 
 bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
-  return ::UnmapPartitionOnDeviceMapper(partition_name);
+  return ::UnmapPartitionWithSuffixOnDeviceMapper(AddSlotSuffix(partition_name));
 }
 
 namespace {  // Ops
@@ -126,22 +136,23 @@
 
 bool PerformOpResize(const OpParameters& params) {
   if (!params.ExpectArgSize(2)) return false;
-  const auto& partition_name = params.arg(0);
+  const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
   auto size = params.uint_arg(1, "size");
   if (!size.has_value()) return false;
 
-  auto partition = params.builder->FindPartition(partition_name);
+  auto partition = params.builder->FindPartition(partition_name_suffix);
   if (partition == nullptr) {
-    LOG(ERROR) << "Failed to find partition " << partition_name
+    LOG(ERROR) << "Failed to find partition " << partition_name_suffix
                << " in dynamic partition metadata.";
     return false;
   }
-  if (!UnmapPartitionOnDeviceMapper(partition_name)) {
-    LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
+  if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
+    LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before resizing.";
     return false;
   }
   if (!params.builder->ResizePartition(partition, size.value())) {
-    LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
+    LOG(ERROR) << "Failed to resize partition " << partition_name_suffix << " to size " << *size
+               << ".";
     return false;
   }
   return true;
@@ -149,24 +160,25 @@
 
 bool PerformOpRemove(const OpParameters& params) {
   if (!params.ExpectArgSize(1)) return false;
-  const auto& partition_name = params.arg(0);
+  const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
 
-  if (!UnmapPartitionOnDeviceMapper(partition_name)) {
-    LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
+  if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
+    LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing.";
     return false;
   }
-  params.builder->RemovePartition(partition_name);
+  params.builder->RemovePartition(partition_name_suffix);
   return true;
 }
 
 bool PerformOpAdd(const OpParameters& params) {
   if (!params.ExpectArgSize(2)) return false;
-  const auto& partition_name = params.arg(0);
-  const auto& group_name = params.arg(1);
+  const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
+  const auto& group_name_suffix = AddSlotSuffix(params.arg(1));
 
-  if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
-      nullptr) {
-    LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
+  if (params.builder->AddPartition(partition_name_suffix, group_name_suffix,
+                                   LP_PARTITION_ATTR_READONLY) == nullptr) {
+    LOG(ERROR) << "Failed to add partition " << partition_name_suffix << " to group "
+               << group_name_suffix << ".";
     return false;
   }
   return true;
@@ -174,21 +186,21 @@
 
 bool PerformOpMove(const OpParameters& params) {
   if (!params.ExpectArgSize(2)) return false;
-  const auto& partition_name = params.arg(0);
-  const auto& new_group = params.arg(1);
+  const auto& partition_name_suffix = AddSlotSuffix(params.arg(0));
+  const auto& new_group_name_suffix = AddSlotSuffix(params.arg(1));
 
-  auto partition = params.builder->FindPartition(partition_name);
+  auto partition = params.builder->FindPartition(partition_name_suffix);
   if (partition == nullptr) {
-    LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
-               << " because it is not found.";
+    LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " to group "
+               << new_group_name_suffix << " because it is not found.";
     return false;
   }
 
-  auto old_group = partition->group_name();
-  if (old_group != new_group) {
-    if (!params.builder->ChangePartitionGroup(partition, new_group)) {
-      LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
-                 << " to group " << new_group << ".";
+  auto old_group_name_suffix = partition->group_name();
+  if (old_group_name_suffix != new_group_name_suffix) {
+    if (!params.builder->ChangePartitionGroup(partition, new_group_name_suffix)) {
+      LOG(ERROR) << "Cannot move partition " << partition_name_suffix << " from group "
+                 << old_group_name_suffix << " to group " << new_group_name_suffix << ".";
       return false;
     }
   }
@@ -197,22 +209,22 @@
 
 bool PerformOpAddGroup(const OpParameters& params) {
   if (!params.ExpectArgSize(2)) return false;
-  const auto& group_name = params.arg(0);
+  const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
   auto maximum_size = params.uint_arg(1, "maximum_size");
   if (!maximum_size.has_value()) return false;
 
-  auto group = params.builder->FindGroup(group_name);
+  auto group = params.builder->FindGroup(group_name_suffix);
   if (group != nullptr) {
-    LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
+    LOG(ERROR) << "Cannot add group " << group_name_suffix << " because it already exists.";
     return false;
   }
 
   if (maximum_size.value() == 0) {
-    LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
+    LOG(WARNING) << "Adding group " << group_name_suffix << " with no size limits.";
   }
 
-  if (!params.builder->AddGroup(group_name, maximum_size.value())) {
-    LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
+  if (!params.builder->AddGroup(group_name_suffix, maximum_size.value())) {
+    LOG(ERROR) << "Failed to add group " << group_name_suffix << " with maximum size "
                << maximum_size.value() << ".";
     return false;
   }
@@ -221,20 +233,20 @@
 
 bool PerformOpResizeGroup(const OpParameters& params) {
   if (!params.ExpectArgSize(2)) return false;
-  const auto& group_name = params.arg(0);
+  const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
   auto new_size = params.uint_arg(1, "maximum_size");
   if (!new_size.has_value()) return false;
 
-  auto group = params.builder->FindGroup(group_name);
+  auto group = params.builder->FindGroup(group_name_suffix);
   if (group == nullptr) {
-    LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
+    LOG(ERROR) << "Cannot resize group " << group_name_suffix << " because it is not found.";
     return false;
   }
 
   auto old_size = group->maximum_size();
   if (old_size != new_size.value()) {
-    if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
-      LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
+    if (!params.builder->ChangeGroupSize(group_name_suffix, new_size.value())) {
+      LOG(ERROR) << "Cannot resize group " << group_name_suffix << " from " << old_size << " to "
                  << new_size.value() << ".";
       return false;
     }
@@ -243,8 +255,8 @@
 }
 
 std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
-                                                   const std::string& group_name) {
-  auto partitions = builder->ListPartitionsInGroup(group_name);
+                                                   const std::string& group_name_suffix) {
+  auto partitions = builder->ListPartitionsInGroup(group_name_suffix);
   std::vector<std::string> partition_names;
   std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
                  [](Partition* partition) { return partition->name(); });
@@ -253,15 +265,16 @@
 
 bool PerformOpRemoveGroup(const OpParameters& params) {
   if (!params.ExpectArgSize(1)) return false;
-  const auto& group_name = params.arg(0);
+  const auto& group_name_suffix = AddSlotSuffix(params.arg(0));
 
-  auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
+  auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
   if (!partition_names.empty()) {
-    LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
+    LOG(ERROR) << "Cannot remove group " << group_name_suffix
+               << " because it still contains partitions ["
                << android::base::Join(partition_names, ", ") << "]";
     return false;
   }
-  params.builder->RemoveGroupAndPartitions(group_name);
+  params.builder->RemoveGroupAndPartitions(group_name_suffix);
   return true;
 }
 
@@ -269,16 +282,16 @@
   if (!params.ExpectArgSize(0)) return false;
 
   auto group_names = params.builder->ListGroups();
-  for (const auto& group_name : group_names) {
-    auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
-    for (const auto& partition_name : partition_names) {
-      if (!UnmapPartitionOnDeviceMapper(partition_name)) {
-        LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
-                   << ".";
+  for (const auto& group_name_suffix : group_names) {
+    auto partition_names = ListPartitionNamesInGroup(params.builder, group_name_suffix);
+    for (const auto& partition_name_suffix : partition_names) {
+      if (!UnmapPartitionWithSuffixOnDeviceMapper(partition_name_suffix)) {
+        LOG(ERROR) << "Cannot unmap " << partition_name_suffix << " before removing group "
+                   << group_name_suffix << ".";
         return false;
       }
     }
-    params.builder->RemoveGroupAndPartitions(group_name);
+    params.builder->RemoveGroupAndPartitions(group_name_suffix);
   }
   return true;
 }