blob: 032ebb14c159bbaef101dd33aa577414fa574621 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
anyhow::Error,
chrono::NaiveDateTime,
fidl::endpoints::create_proxy,
fidl_fuchsia_factory::MiscFactoryStoreProviderProxy,
fidl_fuchsia_intl::{LocaleId, RegulatoryDomain},
fidl_fuchsia_io as fio,
serde::{Deserialize, Serialize},
std::{fs::File, io},
};
// CONFIG AND FACTORY FILE NAMES
const BOARD_CONFIG_JSON_FILE: &str = "/config/data/board_config.json";
const DEFAULT_BOARD_CONFIG_JSON_FILE: &str = "/config/data/default_board_config.json";
const SERIAL_TXT: &str = "serial.txt";
const LOCALE_LIST_FILE: &str = "locale_list.txt";
const HW_TXT: &str = "hw.txt";
const RETAIL_DEMO_FILE: &str = "demo_device";
// CONFIG KEYS
const SKU_KEY: &str = "config";
const LANGUAGE_KEY: &str = "lang";
const REGULATORY_DOMAIN_KEY: &str = "country";
const BUILD_DATE_KEY: &str = "mfg_date";
const BUILD_NAME_KEY: &str = "build_name";
const COLORWAY_KEY: &str = "color";
const DISPLAY_KEY: &str = "display";
const MEMORY_KEY: &str = "memory";
const NAND_STORAGE_KEY: &str = "nand";
const EMMC_STORAGE_KEY: &str = "emmc";
const MICROPHONE_KEY: &str = "mic";
const AUDIO_AMPLIFIER_KEY: &str = "amp";
// CONFIG VALUE FORMAT STRS
const BUILD_DATE_FORMAT_STR: &str = "%Y-%m-%dT%H:%M:%S";
async fn read_factory_file(
path: &str,
proxy_handle: &MiscFactoryStoreProviderProxy,
) -> Result<String, Error> {
let (dir_proxy, dir_server_end) = create_proxy::<fio::DirectoryMarker>()?;
proxy_handle.get_factory_store(dir_server_end)?;
let file_proxy = fuchsia_fs::directory::open_file_no_describe(
&dir_proxy,
path,
fio::OpenFlags::RIGHT_READABLE,
)?;
let result = fuchsia_fs::file::read_to_string(&file_proxy).await?.trim().to_owned();
return Ok(result);
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DeviceInfo {
pub serial_number: Option<String>,
pub is_retail_demo: bool,
pub retail_sku: Option<String>,
}
impl DeviceInfo {
pub async fn load(proxy_handle: &MiscFactoryStoreProviderProxy) -> Self {
let mut device_info =
DeviceInfo { serial_number: None, is_retail_demo: false, retail_sku: None };
device_info.serial_number = match read_factory_file(SERIAL_TXT, proxy_handle).await {
Ok(content) => Some(content),
Err(err) => {
tracing::warn!("Failed to read factory file {}: {}", SERIAL_TXT, err);
None
}
};
if let Ok(content) = read_factory_file(RETAIL_DEMO_FILE, proxy_handle).await {
device_info.is_retail_demo = true;
device_info.retail_sku = Some(content)
};
device_info
}
}
impl Into<fidl_fuchsia_hwinfo::DeviceInfo> for DeviceInfo {
fn into(self) -> fidl_fuchsia_hwinfo::DeviceInfo {
fidl_fuchsia_hwinfo::DeviceInfo {
serial_number: self.serial_number,
is_retail_demo: Some(self.is_retail_demo),
retail_sku: self.retail_sku,
..Default::default()
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Architecture {
X64,
ARM64,
}
impl Into<fidl_fuchsia_hwinfo::Architecture> for Architecture {
fn into(self) -> fidl_fuchsia_hwinfo::Architecture {
match self {
Architecture::X64 => fidl_fuchsia_hwinfo::Architecture::X64,
Architecture::ARM64 => fidl_fuchsia_hwinfo::Architecture::Arm64,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BoardInfo {
pub name: Option<String>,
pub revision: Option<String>,
pub cpu_architecture: Option<Architecture>,
}
impl BoardInfo {
fn get_cpu_architecture() -> Option<Architecture> {
match std::env::consts::ARCH {
"x86_64" => Some(Architecture::X64),
"aarch64" => Some(Architecture::ARM64),
_ => None,
}
}
fn read_config(path: &str) -> Result<Self, Error> {
let board_info: BoardInfo = serde_json::from_reader(io::BufReader::new(File::open(path)?))?;
Ok(board_info)
}
pub fn load() -> Self {
let mut board_info = BoardInfo::read_config(BOARD_CONFIG_JSON_FILE).unwrap_or_else(|err| {
tracing::error!("Failed to read board_config.json due to {}", err);
BoardInfo::read_config(DEFAULT_BOARD_CONFIG_JSON_FILE).unwrap_or_else(|err| {
tracing::error!("Failed to read default_board_config.json due to {}", err);
BoardInfo { name: None, revision: None, cpu_architecture: None }
})
});
board_info.cpu_architecture = BoardInfo::get_cpu_architecture();
board_info
}
}
impl Into<fidl_fuchsia_hwinfo::BoardInfo> for BoardInfo {
fn into(self) -> fidl_fuchsia_hwinfo::BoardInfo {
fidl_fuchsia_hwinfo::BoardInfo {
name: self.name,
revision: self.revision,
cpu_architecture: match self.cpu_architecture {
Some(val) => Some(Architecture::into(val)),
_ => None,
},
..Default::default()
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ConfigFile {
pub name: String,
pub model: String,
pub manufacturer: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ProductInfo {
pub sku: Option<String>,
pub language: Option<String>,
pub country_code: Option<String>,
pub locales: Vec<String>,
pub name: Option<String>,
pub model: Option<String>,
pub manufacturer: Option<String>,
pub build_date: Option<String>,
pub build_name: Option<String>,
pub colorway: Option<String>,
pub display: Option<String>,
pub memory: Option<String>,
pub nand_storage: Option<String>,
pub emmc_storage: Option<String>,
pub microphone: Option<String>,
pub audio_amplifier: Option<String>,
}
impl ProductInfo {
fn new() -> Self {
ProductInfo {
sku: None,
language: None,
country_code: None,
locales: Vec::new(),
name: None,
model: None,
manufacturer: None,
build_date: None,
build_name: None,
colorway: None,
display: None,
memory: None,
nand_storage: None,
emmc_storage: None,
microphone: None,
audio_amplifier: None,
}
}
fn load_from_structured_config(&mut self) {
let hwinfo = hwinfo_structured_config::Config::take_from_startup_handle();
self.name = Some(hwinfo.product_name);
self.model = Some(hwinfo.product_model);
self.manufacturer = Some(hwinfo.product_manufacturer);
}
async fn load_from_hw_file(
&mut self,
path: &str,
proxy_handle: &MiscFactoryStoreProviderProxy,
) -> Result<(), Error> {
let file_content = read_factory_file(path, proxy_handle).await?;
for config in file_content.lines() {
let pair: Vec<_> = config.splitn(2, "=").collect();
let key = pair[0];
let value = pair[1];
let mut filtered_value = value.to_string();
match key {
SKU_KEY => {
self.sku = Some(value.to_owned());
}
LANGUAGE_KEY => {
self.language = Some(value.to_owned());
}
REGULATORY_DOMAIN_KEY => {
self.country_code = Some(value.to_owned());
}
BUILD_DATE_KEY => {
self.build_date = Some(value.to_owned());
filtered_value = NaiveDateTime::parse_from_str(
self.build_date.as_ref().unwrap(),
BUILD_DATE_FORMAT_STR,
)
.map_or_else(
|_| "invalid_format".to_string(),
|datetime| format!("{}", datetime.date().format("%Y-%m-%d")),
);
}
BUILD_NAME_KEY => {
self.build_name = Some(value.to_owned());
}
COLORWAY_KEY => {
self.colorway = Some(value.to_owned());
}
DISPLAY_KEY => {
self.display = Some(value.to_owned());
}
MEMORY_KEY => {
self.memory = Some(value.to_owned());
}
NAND_STORAGE_KEY => {
self.nand_storage = Some(value.to_owned());
}
EMMC_STORAGE_KEY => {
self.emmc_storage = Some(value.to_owned());
}
MICROPHONE_KEY => {
self.microphone = Some(value.to_owned());
}
AUDIO_AMPLIFIER_KEY => {
self.audio_amplifier = Some(value.to_owned());
}
_ => {
tracing::warn!("hw.txt dictionary values {} - {}", key, value.to_owned());
}
}
tracing::warn!("hw.txt line: {}={}", key, filtered_value);
}
Ok(())
}
async fn load_from_locale_list(
&mut self,
path: &str,
proxy_handle: &MiscFactoryStoreProviderProxy,
) -> Result<(), Error> {
let file_content = read_factory_file(path, proxy_handle).await?;
self.locales = Vec::new();
for line in file_content.lines() {
self.locales.push(line.trim().to_owned());
}
Ok(())
}
pub async fn load(proxy_handle: &MiscFactoryStoreProviderProxy) -> Self {
let mut product_info = ProductInfo::new();
product_info.load_from_structured_config();
if let Err(err) = product_info.load_from_hw_file(HW_TXT, proxy_handle).await {
tracing::warn!("Failed to load hw.txt due to {}", err);
}
if let Err(err) = product_info.load_from_locale_list(LOCALE_LIST_FILE, proxy_handle).await {
tracing::warn!("Failed to load locale_list.txt due to {}", err);
}
product_info
}
}
impl Into<fidl_fuchsia_hwinfo::ProductInfo> for ProductInfo {
fn into(self) -> fidl_fuchsia_hwinfo::ProductInfo {
let mut locale_list: Vec<LocaleId> = Vec::new();
for locale in self.locales {
locale_list.push(LocaleId { id: locale.to_owned() });
}
fidl_fuchsia_hwinfo::ProductInfo {
sku: self.sku,
language: self.language,
regulatory_domain: if self.country_code.is_none() {
None
} else {
Some(RegulatoryDomain { country_code: self.country_code, ..Default::default() })
},
locale_list: if locale_list.is_empty() { None } else { Some(locale_list) },
name: self.name,
model: self.model,
manufacturer: self.manufacturer,
build_date: self.build_date,
build_name: self.build_name,
colorway: self.colorway,
display: self.display,
memory: self.memory,
nand_storage: self.nand_storage,
emmc_storage: self.emmc_storage,
microphone: self.microphone,
audio_amplifier: self.audio_amplifier,
..Default::default()
}
}
}