Snap for 14095642 from 1bb0eeb51f4b10884a7833e4045ac9d392439b7f to 25Q4-release

Change-Id: Ib9cf28ed3394d69fdce65a10192e93f211ae34f1
diff --git a/devices/EmulatedCamera/hwl/Android.bp b/devices/EmulatedCamera/hwl/Android.bp
index 5269daf..3ceb0ff 100644
--- a/devices/EmulatedCamera/hwl/Android.bp
+++ b/devices/EmulatedCamera/hwl/Android.bp
@@ -8,6 +8,7 @@
     owner: "google",
     proprietary: true,
     srcs: [
+        "ConfigUtils.cpp",
         "EmulatedCameraProviderHWLImpl.cpp",
         "EmulatedCameraDeviceInfo.cpp",
         "EmulatedCameraDeviceHWLImpl.cpp",
diff --git a/devices/EmulatedCamera/hwl/ConfigUtils.cpp b/devices/EmulatedCamera/hwl/ConfigUtils.cpp
new file mode 100644
index 0000000..e3768ac
--- /dev/null
+++ b/devices/EmulatedCamera/hwl/ConfigUtils.cpp
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2025 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 "EmulatedCameraConfigUtils"
+
+#include "ConfigUtils.h"
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <cutils/properties.h>
+#include <hardware/camera_common.h>
+#include <log/log.h>
+#include <sys/stat.h>
+
+#include "EmulatedSensor.h"
+#include "utils/HWLUtils.h"
+#include "vendor_tag_defs.h"
+
+namespace android {
+
+// Location of the camera configuration files.
+constexpr std::string_view kCameraConfigBack = "emu_camera_back.json";
+constexpr std::string_view kCameraConfigFront = "emu_camera_front.json";
+constexpr std::string_view kCameraConfigExternal = "emu_camera_external.json";
+constexpr std::string_view kCameraConfigDepth = "emu_camera_depth.json";
+constexpr std::string_view kCameraConfigFiles[] = {
+    kCameraConfigBack, kCameraConfigFront, kCameraConfigExternal,
+    kCameraConfigDepth};
+
+constexpr std::string_view kConfigurationFileDirVendor = "/vendor/etc/config/";
+constexpr std::string_view kConfigurationFileDirApex =
+    "/apex/com.google.emulated.camera.provider.hal/etc/config/";
+
+status_t WaitForQemuSfFakeCameraPropertyAvailable() {
+  // Camera service may start running before qemu-props sets
+  // vendor.qemu.sf.fake_camera to any of the following four values:
+  // "none,front,back,both"; so we need to wait.
+  int num_attempts = 100;
+  char prop[PROPERTY_VALUE_MAX];
+  bool timeout = true;
+  for (int i = 0; i < num_attempts; ++i) {
+    if (property_get("vendor.qemu.sf.fake_camera", prop, nullptr) != 0) {
+      timeout = false;
+      break;
+    }
+    usleep(5000);
+  }
+  if (timeout) {
+    ALOGE(
+        "timeout (%dms) waiting for property vendor.qemu.sf.fake_camera to be "
+        "set\n",
+        5 * num_attempts);
+    return BAD_VALUE;
+  }
+  return OK;
+}
+
+bool IsDigit(const std::string& value) {
+  if (value.empty()) {
+    return false;
+  }
+
+  for (const auto& c : value) {
+    if (!std::isdigit(c) && (!std::ispunct(c))) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+template <typename T>
+status_t GetEnumValue(uint32_t tag_id, const char* cstring, T* result /*out*/) {
+  if ((result == nullptr) || (cstring == nullptr)) {
+    return BAD_VALUE;
+  }
+
+  uint32_t enum_value;
+  auto ret =
+      camera_metadata_enum_value(tag_id, cstring, strlen(cstring), &enum_value);
+  if (ret != OK) {
+    ALOGE("%s: Failed to match tag id: 0x%x value: %s", __FUNCTION__, tag_id,
+          cstring);
+    return ret;
+  }
+  *result = enum_value;
+
+  return OK;
+}
+
+status_t GetUInt8Value(const Json::Value& value, uint32_t tag_id,
+                       uint8_t* result /*out*/) {
+  if (result == nullptr) {
+    return BAD_VALUE;
+  }
+
+  if (value.isString()) {
+    errno = 0;
+    if (IsDigit(value.asString())) {
+      auto int_value = strtol(value.asCString(), nullptr, 10);
+      if ((int_value >= 0) && (int_value <= UINT8_MAX) && (errno == 0)) {
+        *result = int_value;
+      } else {
+        ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
+        return BAD_VALUE;
+      }
+    } else {
+      return GetEnumValue(tag_id, value.asCString(), result);
+    }
+  } else {
+    ALOGE(
+        "%s: Unexpected json type: %d! All value types are expected to be "
+        "strings!",
+        __FUNCTION__, value.type());
+    return BAD_VALUE;
+  }
+
+  return OK;
+}
+
+status_t GetInt32Value(const Json::Value& value, uint32_t tag_id,
+                       int32_t* result /*out*/) {
+  if (result == nullptr) {
+    return BAD_VALUE;
+  }
+
+  if (value.isString()) {
+    errno = 0;
+    if (IsDigit(value.asString())) {
+      auto int_value = strtol(value.asCString(), nullptr, 10);
+      if ((int_value >= INT32_MIN) && (int_value <= INT32_MAX) && (errno == 0)) {
+        *result = int_value;
+      } else {
+        ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
+        return BAD_VALUE;
+      }
+    } else {
+      return GetEnumValue(tag_id, value.asCString(), result);
+    }
+  } else {
+    ALOGE(
+        "%s: Unexpected json type: %d! All value types are expected to be "
+        "strings!",
+        __FUNCTION__, value.type());
+    return BAD_VALUE;
+  }
+
+  return OK;
+}
+
+status_t GetInt64Value(const Json::Value& value, uint32_t tag_id,
+                       int64_t* result /*out*/) {
+  if (result == nullptr) {
+    return BAD_VALUE;
+  }
+
+  if (value.isString()) {
+    errno = 0;
+    auto int_value = strtoll(value.asCString(), nullptr, 10);
+    if ((int_value >= INT64_MIN) && (int_value <= INT64_MAX) && (errno == 0)) {
+      *result = int_value;
+    } else {
+      ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
+      return BAD_VALUE;
+    }
+  } else {
+    ALOGE(
+        "%s: Unexpected json type: %d! All value types are expected to be "
+        "strings!",
+        __FUNCTION__, value.type());
+    return BAD_VALUE;
+  }
+
+  return OK;
+}
+
+status_t GetFloatValue(const Json::Value& value, uint32_t tag_id,
+                       float* result /*out*/) {
+  if (result == nullptr) {
+    return BAD_VALUE;
+  }
+
+  if (value.isString()) {
+    errno = 0;
+    auto float_value = strtof(value.asCString(), nullptr);
+    if (errno == 0) {
+      *result = float_value;
+    } else {
+      ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
+      return BAD_VALUE;
+    }
+  } else {
+    ALOGE(
+        "%s: Unexpected json type: %d! All value types are expected to be "
+        "strings!",
+        __FUNCTION__, value.type());
+    return BAD_VALUE;
+  }
+
+  return OK;
+}
+
+status_t GetDoubleValue(const Json::Value& value, uint32_t tag_id,
+                        double* result /*out*/) {
+  if (result == nullptr) {
+    return BAD_VALUE;
+  }
+
+  if (value.isString()) {
+    errno = 0;
+    auto double_value = strtod(value.asCString(), nullptr);
+    if (errno == 0) {
+      *result = double_value;
+    } else {
+      ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
+      return BAD_VALUE;
+    }
+  } else {
+    ALOGE(
+        "%s: Unexpected json type: %d! All value types are expected to be "
+        "strings!",
+        __FUNCTION__, value.type());
+    return BAD_VALUE;
+  }
+
+  return OK;
+}
+
+template <typename T>
+void FilterVendorKeys(uint32_t tag_id, std::vector<T>* values) {
+  if ((values == nullptr) || (values->empty())) {
+    return;
+  }
+
+  switch (tag_id) {
+    case ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS:
+    case ANDROID_REQUEST_AVAILABLE_RESULT_KEYS:
+    case ANDROID_REQUEST_AVAILABLE_SESSION_KEYS:
+    case ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: {
+      auto it = values->begin();
+      while (it != values->end()) {
+        // Per spec. the tags we are handling here will be "int32_t".
+        // In this case all vendor defined values will be negative.
+        if (*it < 0) {
+          it = values->erase(it);
+        } else {
+          it++;
+        }
+      }
+    } break;
+    default:
+      // no-op
+      break;
+  }
+}
+
+template <typename T, typename func_type>
+status_t InsertTag(const Json::Value& json_value, uint32_t tag_id,
+                   func_type get_val_func, HalCameraMetadata* meta /*out*/) {
+  if (meta == nullptr) {
+    return BAD_VALUE;
+  }
+
+  std::vector<T> values;
+  T result;
+  status_t ret = -1;
+  values.reserve(json_value.size());
+  for (const auto& val : json_value) {
+    ret = get_val_func(val, tag_id, &result);
+    if (ret != OK) {
+      break;
+    }
+
+    values.push_back(result);
+  }
+
+  if (ret == OK) {
+    FilterVendorKeys(tag_id, &values);
+    ret = meta->Set(tag_id, values.data(), values.size());
+  }
+
+  return ret;
+}
+
+status_t InsertRationalTag(const Json::Value& json_value, uint32_t tag_id,
+                           HalCameraMetadata* meta /*out*/) {
+  if (meta == nullptr) {
+    return BAD_VALUE;
+  }
+
+  std::vector<camera_metadata_rational_t> values;
+  status_t ret = OK;
+  if (json_value.isArray() && ((json_value.size() % 2) == 0)) {
+    values.reserve(json_value.size() / 2);
+    auto it = json_value.begin();
+    while (it != json_value.end()) {
+      camera_metadata_rational_t result;
+      ret = GetInt32Value((*it), tag_id, &result.numerator);
+      it++;
+      ret |= GetInt32Value((*it), tag_id, &result.denominator);
+      it++;
+      if (ret != OK) {
+        break;
+      }
+      values.push_back(result);
+    }
+  } else {
+    ALOGE("%s: json type: %d doesn't match with rational tag type",
+          __FUNCTION__, json_value.type());
+    return BAD_VALUE;
+  }
+
+  if (ret == OK) {
+    ret = meta->Set(tag_id, values.data(), values.size());
+  }
+
+  return ret;
+}
+
+status_t GetTagFromName(const char* name, uint32_t* tag) {
+  if (name == nullptr || tag == nullptr) {
+    return BAD_VALUE;
+  }
+
+  size_t name_length = strlen(name);
+  // First, find the section by the longest string match
+  const char* section = NULL;
+  size_t section_index = 0;
+  size_t section_length = 0;
+  for (size_t i = 0; i < ANDROID_SECTION_COUNT; ++i) {
+    const char* str = camera_metadata_section_names[i];
+
+    ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
+
+    if (strstr(name, str) == name) {  // name begins with the section name
+      size_t str_length = strlen(str);
+
+      ALOGV("%s: Name begins with section name", __FUNCTION__);
+
+      // section name is the longest we've found so far
+      if (section == NULL || section_length < str_length) {
+        section = str;
+        section_index = i;
+        section_length = str_length;
+
+        ALOGV("%s: Found new best section (%s)", __FUNCTION__, section);
+      }
+    }
+  }
+
+  if (section == NULL) {
+    return NAME_NOT_FOUND;
+  } else {
+    ALOGV("%s: Found matched section '%s' (%zu)", __FUNCTION__, section,
+          section_index);
+  }
+
+  // Get the tag name component of the name
+  const char* name_tag_name = name + section_length + 1;  // x.y.z -> z
+  if (section_length + 1 >= name_length) {
+    return BAD_VALUE;
+  }
+
+  // Match rest of name against the tag names in that section only
+  uint32_t candidate_tag = 0;
+  // Match built-in tags (typically android.*)
+  uint32_t tag_begin, tag_end;  // [tag_begin, tag_end)
+  tag_begin = camera_metadata_section_bounds[section_index][0];
+  tag_end = camera_metadata_section_bounds[section_index][1];
+
+  for (candidate_tag = tag_begin; candidate_tag < tag_end; ++candidate_tag) {
+    const char* tag_name = get_camera_metadata_tag_name(candidate_tag);
+
+    if (strcmp(name_tag_name, tag_name) == 0) {
+      ALOGV("%s: Found matched tag '%s' (%d)", __FUNCTION__, tag_name,
+            candidate_tag);
+      break;
+    }
+  }
+
+  if (candidate_tag == tag_end) {
+    return NAME_NOT_FOUND;
+  }
+
+  *tag = candidate_tag;
+  return OK;
+}
+
+status_t ParseCharacteristics(const Json::Value& value,
+                              CameraConfiguration* config) {
+  if (!value.isObject()) {
+    ALOGE("%s: Configuration root is not an object", __FUNCTION__);
+    return BAD_VALUE;
+  }
+
+  auto static_meta = HalCameraMetadata::Create(1, 10);
+  auto members = value.getMemberNames();
+  for (const auto& member : members) {
+    uint32_t tag_id;
+    auto stat = GetTagFromName(member.c_str(), &tag_id);
+    if (stat != OK) {
+      ALOGE("%s: tag %s not supported, skipping!", __func__, member.c_str());
+      continue;
+    }
+
+    auto tag_type = get_camera_metadata_tag_type(tag_id);
+    auto tag_value = value[member.c_str()];
+    switch (tag_type) {
+      case TYPE_BYTE:
+        InsertTag<uint8_t>(tag_value, tag_id, GetUInt8Value, static_meta.get());
+        break;
+      case TYPE_INT32:
+        InsertTag<int32_t>(tag_value, tag_id, GetInt32Value, static_meta.get());
+        break;
+      case TYPE_INT64:
+        InsertTag<int64_t>(tag_value, tag_id, GetInt64Value, static_meta.get());
+        break;
+      case TYPE_FLOAT:
+        InsertTag<float>(tag_value, tag_id, GetFloatValue, static_meta.get());
+        break;
+      case TYPE_DOUBLE:
+        InsertTag<double>(tag_value, tag_id, GetDoubleValue, static_meta.get());
+        break;
+      case TYPE_RATIONAL:
+        InsertRationalTag(tag_value, tag_id, static_meta.get());
+        break;
+      default:
+        ALOGE("%s: Unsupported tag type: %d!", __FUNCTION__, tag_type);
+    }
+  }
+
+  SensorCharacteristics sensor_characteristics;
+  status_t ret =
+      GetSensorCharacteristics(static_meta.get(), &sensor_characteristics);
+  if (ret != OK) {
+    ALOGE("%s: Unable to extract sensor characteristics!", __FUNCTION__);
+    return ret;
+  }
+
+  if (!EmulatedSensor::AreCharacteristicsSupported(sensor_characteristics)) {
+    ALOGE("%s: Sensor characteristics not supported!", __FUNCTION__);
+    return BAD_VALUE;
+  }
+
+  // Although we don't support HdrPlus, this data is still required by HWL
+  int32_t payload_frames = 0;
+  static_meta->Set(google_camera_hal::kHdrplusPayloadFrames, &payload_frames, 1);
+
+  config->characteristics = std::move(static_meta);
+
+  return OK;
+}
+
+status_t GetCameraConfigurations(std::vector<CameraConfiguration>* configs) {
+  if (configs == nullptr) {
+    return BAD_VALUE;
+  }
+  configs->clear();
+
+  std::string config_content;
+  uint32_t logical_id_counter = 0;
+  uint32_t physical_id_counter = ARRAY_SIZE(kCameraConfigFiles);
+
+  std::vector<std::string> config_file_locations;
+  std::string config_dir = "";
+  struct stat st;
+  if (stat(kConfigurationFileDirApex.data(), &st) == 0) {
+    config_dir += kConfigurationFileDirApex.data();
+  } else {
+    config_dir += kConfigurationFileDirVendor.data();
+  }
+  char prop[PROPERTY_VALUE_MAX];
+  if (!property_get_bool("ro.boot.qemu", false)) {
+    // Cuttlefish
+    property_get("ro.vendor.camera.config", prop, nullptr);
+    if (strcmp(prop, "external") == 0) {
+      config_file_locations.emplace_back(config_dir +
+                                         std::string(kCameraConfigExternal));
+      logical_id_counter = 1;
+    } else {
+      // Default phone layout.
+      config_file_locations.emplace_back(config_dir +
+                                         std::string(kCameraConfigBack));
+      config_file_locations.emplace_back(config_dir +
+                                         std::string(kCameraConfigFront));
+      config_file_locations.emplace_back(config_dir +
+                                         std::string(kCameraConfigDepth));
+    }
+  } else {
+    // Android Studio Emulator
+    if (!property_get_bool("ro.boot.qemu.legacy_fake_camera", false)) {
+      if (WaitForQemuSfFakeCameraPropertyAvailable() == OK) {
+        property_get("vendor.qemu.sf.fake_camera", prop, nullptr);
+        if (strcmp(prop, "both") == 0) {
+          config_file_locations.emplace_back(config_dir +
+                                             std::string(kCameraConfigBack));
+          config_file_locations.emplace_back(config_dir +
+                                             std::string(kCameraConfigFront));
+        } else if (strcmp(prop, "front") == 0) {
+          config_file_locations.emplace_back(config_dir +
+                                             std::string(kCameraConfigFront));
+          logical_id_counter = 1;
+        } else if (strcmp(prop, "back") == 0) {
+          config_file_locations.emplace_back(config_dir +
+                                             std::string(kCameraConfigBack));
+          logical_id_counter = 1;
+        }
+      }
+    }
+  }
+
+  for (const auto& config_path : config_file_locations) {
+    if (!android::base::ReadFileToString(config_path, &config_content)) {
+      ALOGW("%s: Could not open configuration file: %s", __FUNCTION__,
+            config_path.c_str());
+      return BAD_VALUE;
+    }
+
+    Json::CharReaderBuilder builder;
+    std::unique_ptr<Json::CharReader> config_reader(builder.newCharReader());
+    Json::Value root;
+    std::string error_message;
+    if (!config_reader->parse(&*config_content.begin(), &*config_content.end(),
+                              &root, &error_message)) {
+      ALOGE("Could not parse configuration file: %s", error_message.c_str());
+      return BAD_VALUE;
+    }
+
+    CameraConfiguration cam_config;
+    cam_config.id = logical_id_counter;
+
+    if (root.isArray()) {
+      auto device_iter = root.begin();
+      if (ParseCharacteristics(*device_iter, &cam_config) != OK) {
+        return BAD_VALUE;
+      }
+      device_iter++;
+
+      if (root.size() >= 3) {
+        while (device_iter != root.end()) {
+          CameraConfiguration physical_config;
+          if (ParseCharacteristics(*device_iter, &physical_config) == OK) {
+            cam_config.physical_camera_characteristics[physical_id_counter++] =
+                std::move(physical_config.characteristics);
+          }
+          device_iter++;
+        }
+      }
+    } else {
+      if (ParseCharacteristics(root, &cam_config) != OK) {
+        return BAD_VALUE;
+      }
+    }
+    configs->push_back(std::move(cam_config));
+    logical_id_counter++;
+  }
+
+  return OK;
+}
+
+}  // namespace android
diff --git a/devices/EmulatedCamera/hwl/ConfigUtils.h b/devices/EmulatedCamera/hwl/ConfigUtils.h
new file mode 100644
index 0000000..8729ce1
--- /dev/null
+++ b/devices/EmulatedCamera/hwl/ConfigUtils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 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 EMULATOR_CAMERA_HAL_HWL_CONFIG_UTILS_H
+#define EMULATOR_CAMERA_HAL_HWL_CONFIG_UTILS_H
+
+#include <hal_camera_metadata.h>
+#include <json/json.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android {
+
+using google_camera_hal::HalCameraMetadata;
+
+struct CameraConfiguration {
+  uint32_t id;
+  // The main metadata for this camera (logical or physical).
+  std::unique_ptr<HalCameraMetadata> characteristics;
+  // For logical cameras, this will be populated with the characteristics of the
+  // underlying physical cameras.
+  std::map<uint32_t, std::unique_ptr<HalCameraMetadata>>
+      physical_camera_characteristics;
+};
+
+status_t GetCameraConfigurations(std::vector<CameraConfiguration>* configs);
+
+}  // namespace android
+
+#endif  // EMULATOR_CAMERA_HAL_HWL_CONFIG_UTILS_H
diff --git a/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.cpp b/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.cpp
index 837191f..80814a6 100644
--- a/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.cpp
+++ b/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.cpp
@@ -24,6 +24,7 @@
 #include <hardware/camera_common.h>
 #include <log/log.h>
 
+#include "ConfigUtils.h"
 #include "EmulatedCameraDeviceHWLImpl.h"
 #include "EmulatedCameraDeviceSessionHWLImpl.h"
 #include "EmulatedLogicalRequestState.h"
@@ -34,19 +35,6 @@
 
 namespace android {
 
-// Location of the camera configuration files.
-constexpr std::string_view kCameraConfigBack = "emu_camera_back.json";
-constexpr std::string_view kCameraConfigFront = "emu_camera_front.json";
-constexpr std::string_view kCameraConfigExternal = "emu_camera_external.json";
-constexpr std::string_view kCameraConfigDepth = "emu_camera_depth.json";
-constexpr std::string_view kCameraConfigFiles[] = {
-    kCameraConfigBack, kCameraConfigFront, kCameraConfigExternal,
-    kCameraConfigDepth};
-
-constexpr std::string_view kConfigurationFileDirVendor = "/vendor/etc/config/";
-constexpr std::string_view kConfigurationFileDirApex =
-    "/apex/com.google.emulated.camera.provider.hal/etc/config/";
-
 constexpr StreamSize s240pStreamSize = std::pair(240, 180);
 constexpr StreamSize s720pStreamSize = std::pair(1280, 720);
 constexpr StreamSize s1440pStreamSize = std::pair(1920, 1440);
@@ -73,76 +61,6 @@
   return provider;
 }
 
-status_t EmulatedCameraProviderHwlImpl::GetTagFromName(const char* name,
-                                                       uint32_t* tag) {
-  if (name == nullptr || tag == nullptr) {
-    return BAD_VALUE;
-  }
-
-  size_t name_length = strlen(name);
-  // First, find the section by the longest string match
-  const char* section = NULL;
-  size_t section_index = 0;
-  size_t section_length = 0;
-  for (size_t i = 0; i < ANDROID_SECTION_COUNT; ++i) {
-    const char* str = camera_metadata_section_names[i];
-
-    ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
-
-    if (strstr(name, str) == name) {  // name begins with the section name
-      size_t str_length = strlen(str);
-
-      ALOGV("%s: Name begins with section name", __FUNCTION__);
-
-      // section name is the longest we've found so far
-      if (section == NULL || section_length < str_length) {
-        section = str;
-        section_index = i;
-        section_length = str_length;
-
-        ALOGV("%s: Found new best section (%s)", __FUNCTION__, section);
-      }
-    }
-  }
-
-  if (section == NULL) {
-    return NAME_NOT_FOUND;
-  } else {
-    ALOGV("%s: Found matched section '%s' (%zu)", __FUNCTION__, section,
-          section_index);
-  }
-
-  // Get the tag name component of the name
-  const char* name_tag_name = name + section_length + 1;  // x.y.z -> z
-  if (section_length + 1 >= name_length) {
-    return BAD_VALUE;
-  }
-
-  // Match rest of name against the tag names in that section only
-  uint32_t candidate_tag = 0;
-  // Match built-in tags (typically android.*)
-  uint32_t tag_begin, tag_end;  // [tag_begin, tag_end)
-  tag_begin = camera_metadata_section_bounds[section_index][0];
-  tag_end = camera_metadata_section_bounds[section_index][1];
-
-  for (candidate_tag = tag_begin; candidate_tag < tag_end; ++candidate_tag) {
-    const char* tag_name = get_camera_metadata_tag_name(candidate_tag);
-
-    if (strcmp(name_tag_name, tag_name) == 0) {
-      ALOGV("%s: Found matched tag '%s' (%d)", __FUNCTION__, tag_name,
-            candidate_tag);
-      break;
-    }
-  }
-
-  if (candidate_tag == tag_end) {
-    return NAME_NOT_FOUND;
-  }
-
-  *tag = candidate_tag;
-  return OK;
-}
-
 static bool IsMaxSupportedSizeGreaterThanOrEqual(
     const std::set<StreamSize>& stream_sizes, StreamSize compare_size) {
   for (const auto& stream_size : stream_sizes) {
@@ -173,7 +91,7 @@
 
 bool EmulatedCameraProviderHwlImpl::SupportsMandatoryConcurrentStreams(
     uint32_t camera_id) {
-  HalCameraMetadata& static_metadata = *(static_metadata_[camera_id]);
+  HalCameraMetadata& static_metadata = *(static_metadata_.at(camera_id));
   auto map = std::make_unique<StreamConfigurationMap>(static_metadata);
   auto yuv_output_sizes = map->GetOutputSizes(HAL_PIXEL_FORMAT_YCBCR_420_888);
   auto blob_output_sizes = map->GetOutputSizes(HAL_PIXEL_FORMAT_BLOB);
@@ -269,9 +187,9 @@
   // make all possible combinations since it should be possible to stream all
   // of them at once in the emulated camera.
   std::unordered_set<uint32_t> candidate_ids;
-  for (auto& entry : camera_id_map_) {
-    if (SupportsMandatoryConcurrentStreams(entry.first)) {
-      candidate_ids.insert(entry.first);
+  for (auto const& [camera_id, _] : camera_id_map_) {
+    if (SupportsMandatoryConcurrentStreams(camera_id)) {
+      candidate_ids.insert(camera_id);
     }
   }
   combinations->emplace_back(std::move(candidate_ids));
@@ -290,44 +208,45 @@
   // Go through the given camera ids, get their sensor characteristics, stream
   // config maps and call EmulatedSensor::IsStreamCombinationSupported()
   for (auto& config : configs) {
+    uint32_t camera_id = config.camera_id;
     // TODO: Consider caching sensor characteristics and StreamConfigurationMap
-    if (camera_id_map_.find(config.camera_id) == camera_id_map_.end()) {
-      ALOGE("%s: Camera id %u does not exist", __FUNCTION__, config.camera_id);
+    if (camera_id_map_.find(camera_id) == camera_id_map_.end()) {
+      ALOGE("%s: Camera id %u does not exist", __FUNCTION__, camera_id);
       return BAD_VALUE;
     }
 
     auto stream_configuration_map = std::make_unique<StreamConfigurationMap>(
-        *(static_metadata_[config.camera_id]));
+        *(static_metadata_.at(camera_id)));
     auto stream_configuration_map_max_resolution =
         std::make_unique<StreamConfigurationMap>(
-            *(static_metadata_[config.camera_id]), /*maxResolution*/ true);
+            *(static_metadata_.at(camera_id)), /*maxResolution*/ true);
 
     LogicalCharacteristics sensor_chars;
-    status_t ret =
-        GetSensorCharacteristics((static_metadata_[config.camera_id]).get(),
-                                 &sensor_chars[config.camera_id]);
+    status_t ret = GetSensorCharacteristics(
+        (static_metadata_.at(camera_id)).get(), &sensor_chars[camera_id]);
     if (ret != OK) {
       ALOGE("%s: Unable to extract sensor chars for camera id %u", __FUNCTION__,
-            config.camera_id);
+            camera_id);
       return UNKNOWN_ERROR;
     }
 
     PhysicalStreamConfigurationMap physical_stream_configuration_map;
     PhysicalStreamConfigurationMap physical_stream_configuration_map_max_resolution;
-    auto const& physicalCameraInfo = camera_id_map_[config.camera_id];
+    auto const& physicalCameraInfo = camera_id_map_.at(camera_id);
     for (size_t i = 0; i < physicalCameraInfo.size(); i++) {
       uint32_t physical_camera_id = physicalCameraInfo[i].second;
       physical_stream_configuration_map.emplace(
           physical_camera_id, std::make_unique<StreamConfigurationMap>(
-                                  *(static_metadata_[physical_camera_id])));
+                                  *(static_metadata_.at(physical_camera_id))));
 
       physical_stream_configuration_map_max_resolution.emplace(
-          physical_camera_id,
-          std::make_unique<StreamConfigurationMap>(
-              *(static_metadata_[physical_camera_id]), /*maxResolution*/ true));
+          physical_camera_id, std::make_unique<StreamConfigurationMap>(
+                                  *(static_metadata_.at(physical_camera_id)),
+                                  /*maxResolution*/ true));
 
-      ret = GetSensorCharacteristics(static_metadata_[physical_camera_id].get(),
-                                     &sensor_chars[physical_camera_id]);
+      ret = GetSensorCharacteristics(
+          static_metadata_.at(physical_camera_id).get(),
+          &sensor_chars[physical_camera_id]);
       if (ret != OK) {
         ALOGE("%s: Unable to extract camera %d sensor characteristics %s (%d)",
               __FUNCTION__, physical_camera_id, strerror(-ret), ret);
@@ -336,8 +255,8 @@
     }
 
     if (!EmulatedSensor::IsStreamCombinationSupported(
-            config.camera_id, config.stream_configuration,
-            *stream_configuration_map, *stream_configuration_map_max_resolution,
+            camera_id, config.stream_configuration, *stream_configuration_map,
+            *stream_configuration_map_max_resolution,
             physical_stream_configuration_map,
             physical_stream_configuration_map_max_resolution, sensor_chars)) {
       return OK;
@@ -348,484 +267,55 @@
   return OK;
 }
 
-bool IsDigit(const std::string& value) {
-  if (value.empty()) {
-    return false;
-  }
-
-  for (const auto& c : value) {
-    if (!std::isdigit(c) && (!std::ispunct(c))) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-template <typename T>
-status_t GetEnumValue(uint32_t tag_id, const char* cstring, T* result /*out*/) {
-  if ((result == nullptr) || (cstring == nullptr)) {
-    return BAD_VALUE;
-  }
-
-  uint32_t enum_value;
-  auto ret =
-      camera_metadata_enum_value(tag_id, cstring, strlen(cstring), &enum_value);
-  if (ret != OK) {
-    ALOGE("%s: Failed to match tag id: 0x%x value: %s", __FUNCTION__, tag_id,
-          cstring);
-    return ret;
-  }
-  *result = enum_value;
-
-  return OK;
-}
-
-status_t GetUInt8Value(const Json::Value& value, uint32_t tag_id,
-                       uint8_t* result /*out*/) {
-  if (result == nullptr) {
-    return BAD_VALUE;
-  }
-
-  if (value.isString()) {
-    errno = 0;
-    if (IsDigit(value.asString())) {
-      auto int_value = strtol(value.asCString(), nullptr, 10);
-      if ((int_value >= 0) && (int_value <= UINT8_MAX) && (errno == 0)) {
-        *result = int_value;
-      } else {
-        ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
-        return BAD_VALUE;
-      }
-    } else {
-      return GetEnumValue(tag_id, value.asCString(), result);
-    }
-  } else {
-    ALOGE(
-        "%s: Unexpected json type: %d! All value types are expected to be "
-        "strings!",
-        __FUNCTION__, value.type());
-    return BAD_VALUE;
-  }
-
-  return OK;
-}
-
-status_t GetInt32Value(const Json::Value& value, uint32_t tag_id,
-                       int32_t* result /*out*/) {
-  if (result == nullptr) {
-    return BAD_VALUE;
-  }
-
-  if (value.isString()) {
-    errno = 0;
-    if (IsDigit(value.asString())) {
-      auto int_value = strtol(value.asCString(), nullptr, 10);
-      if ((int_value >= INT32_MIN) && (int_value <= INT32_MAX) && (errno == 0)) {
-        *result = int_value;
-      } else {
-        ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
-        return BAD_VALUE;
-      }
-    } else {
-      return GetEnumValue(tag_id, value.asCString(), result);
-    }
-  } else {
-    ALOGE(
-        "%s: Unexpected json type: %d! All value types are expected to be "
-        "strings!",
-        __FUNCTION__, value.type());
-    return BAD_VALUE;
-  }
-
-  return OK;
-}
-
-status_t GetInt64Value(const Json::Value& value, uint32_t tag_id,
-                       int64_t* result /*out*/) {
-  if (result == nullptr) {
-    return BAD_VALUE;
-  }
-
-  if (value.isString()) {
-    errno = 0;
-    auto int_value = strtoll(value.asCString(), nullptr, 10);
-    if ((int_value >= INT64_MIN) && (int_value <= INT64_MAX) && (errno == 0)) {
-      *result = int_value;
-    } else {
-      ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
-      return BAD_VALUE;
-    }
-  } else {
-    ALOGE(
-        "%s: Unexpected json type: %d! All value types are expected to be "
-        "strings!",
-        __FUNCTION__, value.type());
-    return BAD_VALUE;
-  }
-
-  return OK;
-}
-
-status_t GetFloatValue(const Json::Value& value, uint32_t tag_id,
-                       float* result /*out*/) {
-  if (result == nullptr) {
-    return BAD_VALUE;
-  }
-
-  if (value.isString()) {
-    errno = 0;
-    auto float_value = strtof(value.asCString(), nullptr);
-    if (errno == 0) {
-      *result = float_value;
-    } else {
-      ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
-      return BAD_VALUE;
-    }
-  } else {
-    ALOGE(
-        "%s: Unexpected json type: %d! All value types are expected to be "
-        "strings!",
-        __FUNCTION__, value.type());
-    return BAD_VALUE;
-  }
-
-  return OK;
-}
-
-status_t GetDoubleValue(const Json::Value& value, uint32_t tag_id,
-                        double* result /*out*/) {
-  if (result == nullptr) {
-    return BAD_VALUE;
-  }
-
-  if (value.isString()) {
-    errno = 0;
-    auto double_value = strtod(value.asCString(), nullptr);
-    if (errno == 0) {
-      *result = double_value;
-    } else {
-      ALOGE("%s: Failed parsing tag id 0x%x", __func__, tag_id);
-      return BAD_VALUE;
-    }
-  } else {
-    ALOGE(
-        "%s: Unexpected json type: %d! All value types are expected to be "
-        "strings!",
-        __FUNCTION__, value.type());
-    return BAD_VALUE;
-  }
-
-  return OK;
-}
-
-template <typename T>
-void FilterVendorKeys(uint32_t tag_id, std::vector<T>* values) {
-  if ((values == nullptr) || (values->empty())) {
-    return;
-  }
-
-  switch (tag_id) {
-    case ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS:
-    case ANDROID_REQUEST_AVAILABLE_RESULT_KEYS:
-    case ANDROID_REQUEST_AVAILABLE_SESSION_KEYS:
-    case ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: {
-      auto it = values->begin();
-      while (it != values->end()) {
-        // Per spec. the tags we are handling here will be "int32_t".
-        // In this case all vendor defined values will be negative.
-        if (*it < 0) {
-          it = values->erase(it);
-        } else {
-          it++;
-        }
-      }
-    } break;
-    default:
-      // no-op
-      break;
-  }
-}
-
-template <typename T, typename func_type>
-status_t InsertTag(const Json::Value& json_value, uint32_t tag_id,
-                   func_type get_val_func, HalCameraMetadata* meta /*out*/) {
-  if (meta == nullptr) {
-    return BAD_VALUE;
-  }
-
-  std::vector<T> values;
-  T result;
-  status_t ret = -1;
-  values.reserve(json_value.size());
-  for (const auto& val : json_value) {
-    ret = get_val_func(val, tag_id, &result);
-    if (ret != OK) {
-      break;
-    }
-
-    values.push_back(result);
-  }
-
-  if (ret == OK) {
-    FilterVendorKeys(tag_id, &values);
-    ret = meta->Set(tag_id, values.data(), values.size());
-  }
-
-  return ret;
-}
-
-status_t InsertRationalTag(const Json::Value& json_value, uint32_t tag_id,
-                           HalCameraMetadata* meta /*out*/) {
-  if (meta == nullptr) {
-    return BAD_VALUE;
-  }
-
-  std::vector<camera_metadata_rational_t> values;
-  status_t ret = OK;
-  if (json_value.isArray() && ((json_value.size() % 2) == 0)) {
-    values.reserve(json_value.size() / 2);
-    auto it = json_value.begin();
-    while (it != json_value.end()) {
-      camera_metadata_rational_t result;
-      ret = GetInt32Value((*it), tag_id, &result.numerator);
-      it++;
-      ret |= GetInt32Value((*it), tag_id, &result.denominator);
-      it++;
-      if (ret != OK) {
-        break;
-      }
-      values.push_back(result);
-    }
-  } else {
-    ALOGE("%s: json type: %d doesn't match with rational tag type",
-          __FUNCTION__, json_value.type());
-    return BAD_VALUE;
-  }
-
-  if (ret == OK) {
-    ret = meta->Set(tag_id, values.data(), values.size());
-  }
-
-  return ret;
-}
-
-uint32_t EmulatedCameraProviderHwlImpl::ParseCharacteristics(
-    const Json::Value& value, ssize_t id) {
-  if (!value.isObject()) {
-    ALOGE("%s: Configuration root is not an object", __FUNCTION__);
-    return BAD_VALUE;
-  }
-
-  auto static_meta = HalCameraMetadata::Create(1, 10);
-  auto members = value.getMemberNames();
-  for (const auto& member : members) {
-    uint32_t tag_id;
-    auto stat = GetTagFromName(member.c_str(), &tag_id);
-    if (stat != OK) {
-      ALOGE("%s: tag %s not supported, skipping!", __func__, member.c_str());
-      continue;
-    }
-
-    auto tag_type = get_camera_metadata_tag_type(tag_id);
-    auto tag_value = value[member.c_str()];
-    switch (tag_type) {
-      case TYPE_BYTE:
-        InsertTag<uint8_t>(tag_value, tag_id, GetUInt8Value, static_meta.get());
-        break;
-      case TYPE_INT32:
-        InsertTag<int32_t>(tag_value, tag_id, GetInt32Value, static_meta.get());
-        break;
-      case TYPE_INT64:
-        InsertTag<int64_t>(tag_value, tag_id, GetInt64Value, static_meta.get());
-        break;
-      case TYPE_FLOAT:
-        InsertTag<float>(tag_value, tag_id, GetFloatValue, static_meta.get());
-        break;
-      case TYPE_DOUBLE:
-        InsertTag<double>(tag_value, tag_id, GetDoubleValue, static_meta.get());
-        break;
-      case TYPE_RATIONAL:
-        InsertRationalTag(tag_value, tag_id, static_meta.get());
-        break;
-      default:
-        ALOGE("%s: Unsupported tag type: %d!", __FUNCTION__, tag_type);
-    }
-  }
-
-  SensorCharacteristics sensor_characteristics;
-  auto ret =
-      GetSensorCharacteristics(static_meta.get(), &sensor_characteristics);
-  if (ret != OK) {
-    ALOGE("%s: Unable to extract sensor characteristics!", __FUNCTION__);
-    return ret;
-  }
-
-  if (!EmulatedSensor::AreCharacteristicsSupported(sensor_characteristics)) {
-    ALOGE("%s: Sensor characteristics not supported!", __FUNCTION__);
-    return BAD_VALUE;
-  }
-
-  // Although we don't support HdrPlus, this data is still required by HWL
-  int32_t payload_frames = 0;
-  static_meta->Set(google_camera_hal::kHdrplusPayloadFrames, &payload_frames, 1);
-
-  if (id < 0) {
-    static_metadata_.push_back(std::move(static_meta));
-    id = static_metadata_.size() - 1;
-  } else {
-    static_metadata_[id] = std::move(static_meta);
-  }
-
-  return id;
-}
-
-status_t EmulatedCameraProviderHwlImpl::WaitForQemuSfFakeCameraPropertyAvailable() {
-  // Camera service may start running before qemu-props sets
-  // vendor.qemu.sf.fake_camera to any of the following four values:
-  // "none,front,back,both"; so we need to wait.
-  int num_attempts = 100;
-  char prop[PROPERTY_VALUE_MAX];
-  bool timeout = true;
-  for (int i = 0; i < num_attempts; ++i) {
-    if (property_get("vendor.qemu.sf.fake_camera", prop, nullptr) != 0) {
-      timeout = false;
-      break;
-    }
-    usleep(5000);
-  }
-  if (timeout) {
-    ALOGE("timeout (%dms) waiting for property vendor.qemu.sf.fake_camera to be set\n",
-          5 * num_attempts);
-    return BAD_VALUE;
-  }
-  return OK;
-}
-
 status_t EmulatedCameraProviderHwlImpl::Initialize() {
-  // GCH expects all physical ids to be bigger than the logical ones.
-  // Resize 'static_metadata_' to fit all logical devices and insert them
-  // accordingly, push any remaining physical cameras in the back.
-  std::string config;
-  size_t logical_id = 0;
-  std::vector<std::string> config_file_locations;
-  std::string config_dir = "";
-  struct stat st;
-  if (stat(kConfigurationFileDirApex.data(), &st) == 0) {
-    config_dir += kConfigurationFileDirApex.data();
-  } else {
-    config_dir += kConfigurationFileDirVendor.data();
+  std::vector<CameraConfiguration> camera_configs;
+  status_t res = GetCameraConfigurations(&camera_configs);
+  if (res != OK) {
+    return res;
   }
-  char prop[PROPERTY_VALUE_MAX];
-  if (!property_get_bool("ro.boot.qemu", false)) {
-    // Cuttlefish
-    property_get("ro.vendor.camera.config", prop, nullptr);
-    if (strcmp(prop, "external") == 0) {
-      config_file_locations.emplace_back(config_dir +
-                                         kCameraConfigExternal.data());
-      logical_id = 1;
-    } else {
-      // Default phone layout.
-      config_file_locations.emplace_back(config_dir + kCameraConfigBack.data());
-      config_file_locations.emplace_back(config_dir + kCameraConfigFront.data());
-      config_file_locations.emplace_back(config_dir + kCameraConfigDepth.data());
-    }
-  } else {
-    // Android Studio Emulator
-    if (!property_get_bool("ro.boot.qemu.legacy_fake_camera", false)) {
-      if (WaitForQemuSfFakeCameraPropertyAvailable() == OK) {
-        property_get("vendor.qemu.sf.fake_camera", prop, nullptr);
-        if (strcmp(prop, "both") == 0) {
-          config_file_locations.emplace_back(config_dir +
-                                             kCameraConfigBack.data());
-          config_file_locations.emplace_back(config_dir +
-                                             kCameraConfigFront.data());
-        } else if (strcmp(prop, "front") == 0) {
-          config_file_locations.emplace_back(config_dir +
-                                             kCameraConfigFront.data());
-          logical_id = 1;
-        } else if (strcmp(prop, "back") == 0) {
-          config_file_locations.emplace_back(config_dir +
-                                             kCameraConfigBack.data());
-          logical_id = 1;
-        }
+
+  for (auto& config : camera_configs) {
+    static_metadata_[config.id] = std::move(config.characteristics);
+    if (!config.physical_camera_characteristics.empty()) {
+      // This is a logical camera
+      std::vector<std::pair<CameraDeviceStatus, uint32_t>> physical_ids;
+      size_t physical_device_count = 0;
+      for (auto const& [physical_id, physical_chars] :
+           config.physical_camera_characteristics) {
+        static_metadata_[physical_id] =
+            HalCameraMetadata::Clone(physical_chars.get());
+        auto device_status = (physical_device_count < 2)
+                                 ? CameraDeviceStatus::kPresent
+                                 : CameraDeviceStatus::kNotPresent;
+        physical_ids.push_back({device_status, physical_id});
+        physical_device_count++;
       }
-    }
-  }
-  static_metadata_.resize(ARRAY_SIZE(kCameraConfigFiles));
+      camera_id_map_[config.id] = std::move(physical_ids);
 
-  for (const auto& config_path : config_file_locations) {
-    if (!android::base::ReadFileToString(config_path, &config)) {
-      ALOGW("%s: Could not open configuration file: %s", __FUNCTION__,
-            config_path.c_str());
-      continue;
-    }
-
-    Json::CharReaderBuilder builder;
-    std::unique_ptr<Json::CharReader> config_reader(builder.newCharReader());
-    Json::Value root;
-    std::string error_message;
-    if (!config_reader->parse(&*config.begin(), &*config.end(), &root,
-                              &error_message)) {
-      ALOGE("Could not parse configuration file: %s", error_message.c_str());
-      return BAD_VALUE;
-    }
-
-    if (root.isArray()) {
-      auto device_iter = root.begin();
-      auto result_id = ParseCharacteristics(*device_iter, logical_id);
-      if (logical_id != result_id) {
-        return result_id;
+      auto physical_devices = std::make_unique<PhysicalDeviceMap>();
+      for (const auto& physical_device : camera_id_map_.at(config.id)) {
+        physical_devices->emplace(
+            physical_device.second,
+            std::make_pair(
+                physical_device.first,
+                HalCameraMetadata::Clone(
+                    static_metadata_.at(physical_device.second).get())));
       }
-      device_iter++;
-
-      // The first device entry is always the logical camera followed by the
-      // physical devices. They must be at least 2.
-      camera_id_map_.emplace(logical_id, std::vector<std::pair<CameraDeviceStatus, uint32_t>>());
-      if (root.size() >= 3) {
-        camera_id_map_[logical_id].reserve(root.size() - 1);
-        size_t current_physical_device = 0;
-        while (device_iter != root.end()) {
-          auto physical_id = ParseCharacteristics(*device_iter, /*id*/ -1);
-          if (physical_id < 0) {
-            return physical_id;
-          }
-          // Only notify unavailable physical camera if there are more than 2
-          // physical cameras backing the logical camera
-          auto device_status = (current_physical_device < 2) ? CameraDeviceStatus::kPresent :
-              CameraDeviceStatus::kNotPresent;
-          camera_id_map_[logical_id].push_back(std::make_pair(device_status, physical_id));
-          device_iter++; current_physical_device++;
-        }
-
-        auto physical_devices = std::make_unique<PhysicalDeviceMap>();
-        for (const auto& physical_device : camera_id_map_[logical_id]) {
-          physical_devices->emplace(
-              physical_device.second, std::make_pair(physical_device.first,
-              HalCameraMetadata::Clone(
-                  static_metadata_[physical_device.second].get())));
-        }
-        auto updated_logical_chars =
-            EmulatedLogicalRequestState::AdaptLogicalCharacteristics(
-                HalCameraMetadata::Clone(static_metadata_[logical_id].get()),
-                std::move(physical_devices));
-        if (updated_logical_chars.get() != nullptr) {
-          static_metadata_[logical_id].swap(updated_logical_chars);
-        } else {
-          ALOGE("%s: Failed to updating logical camera characteristics!",
-                __FUNCTION__);
-          return BAD_VALUE;
-        }
+      auto updated_logical_chars =
+          EmulatedLogicalRequestState::AdaptLogicalCharacteristics(
+              HalCameraMetadata::Clone(static_metadata_.at(config.id).get()),
+              std::move(physical_devices));
+      if (updated_logical_chars.get() != nullptr) {
+        static_metadata_.at(config.id).swap(updated_logical_chars);
+      } else {
+        ALOGE(
+            "%s: Failed to updating logical camera characteristics for id %u!",
+            __FUNCTION__, config.id);
+        return BAD_VALUE;
       }
     } else {
-      auto result_id = ParseCharacteristics(root, logical_id);
-      if (result_id != logical_id) {
-        return result_id;
-      }
-      camera_id_map_.emplace(logical_id, std::vector<std::pair<CameraDeviceStatus, uint32_t>>());
+      camera_id_map_[config.id] = {};
     }
-
-    logical_id++;
   }
 
   return OK;
@@ -890,7 +380,7 @@
 }
 
 status_t EmulatedCameraProviderHwlImpl::GetVisibleCameraIds(
-    std::vector<std::uint32_t>* camera_ids) {
+    std::vector<uint32_t>* camera_ids) {
   if (camera_ids == nullptr) {
     ALOGE("%s: camera_ids is nullptr.", __FUNCTION__);
     return BAD_VALUE;
@@ -916,7 +406,7 @@
   }
 
   std::unique_ptr<HalCameraMetadata> meta =
-      HalCameraMetadata::Clone(static_metadata_[camera_id].get());
+      HalCameraMetadata::Clone(static_metadata_.at(camera_id).get());
 
   std::shared_ptr<EmulatedTorchState> torch_state;
   camera_metadata_ro_entry entry;
@@ -933,10 +423,12 @@
   }
 
   auto physical_devices = std::make_unique<PhysicalDeviceMap>();
-  for (const auto& physical_device : camera_id_map_[camera_id]) {
-      physical_devices->emplace(
-          physical_device.second, std::make_pair(physical_device.first,
-          HalCameraMetadata::Clone(static_metadata_[physical_device.second].get())));
+  for (const auto& physical_device : camera_id_map_.at(camera_id)) {
+    physical_devices->emplace(
+        physical_device.second,
+        std::make_pair(physical_device.first,
+                       HalCameraMetadata::Clone(
+                           static_metadata_.at(physical_device.second).get())));
   }
   *camera_device_hwl = EmulatedCameraDeviceHwlImpl::Create(
       camera_id, std::move(meta), std::move(physical_devices), torch_state);
diff --git a/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.h b/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.h
index f0f7589..64ebb18 100644
--- a/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.h
+++ b/devices/EmulatedCamera/hwl/EmulatedCameraProviderHWLImpl.h
@@ -19,8 +19,7 @@
 
 #include <camera_provider_hwl.h>
 #include <hal_types.h>
-#include <json/json.h>
-#include <json/reader.h>
+
 #include <future>
 
 namespace android {
@@ -80,15 +79,15 @@
 
  private:
   status_t Initialize();
-  uint32_t ParseCharacteristics(const Json::Value& root, ssize_t id);
-  status_t GetTagFromName(const char* name, uint32_t* tag);
-  status_t WaitForQemuSfFakeCameraPropertyAvailable();
   bool SupportsMandatoryConcurrentStreams(uint32_t camera_id);
 
-  std::vector<std::unique_ptr<HalCameraMetadata>> static_metadata_;
+  // Static camera metadata, keyed by camera id.
+  std::unordered_map<uint32_t, std::unique_ptr<HalCameraMetadata>> static_metadata_;
   // Logical to physical camera Id mapping. Empty value vector in case
   // of regular non-logical device.
-  std::unordered_map<uint32_t, std::vector<std::pair<CameraDeviceStatus, uint32_t>>> camera_id_map_;
+  std::unordered_map<uint32_t,
+                     std::vector<std::pair<CameraDeviceStatus, uint32_t>>>
+      camera_id_map_;
   HwlTorchModeStatusChangeFunc torch_cb_;
   HwlPhysicalCameraDeviceStatusChangeFunc physical_camera_status_cb_;