blob: 1f6195b3dc6aadd9bce3492825d08868e6328a03 [file] [log] [blame] [edit]
// 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,
fidl::endpoints::create_proxy,
fidl_fuchsia_factory::MiscFactoryStoreProviderProxy,
fidl_fuchsia_hwinfo,
fidl_fuchsia_intl::{LocaleId, RegulatoryDomain},
fidl_fuchsia_io::{DirectoryMarker, OPEN_RIGHT_READABLE},
fuchsia_syslog::{self, fx_log_err, fx_log_warn},
serde::{Deserialize, Serialize},
std::{fs::File, io, path::Path},
};
// CONFIG AND FACTORY FILE NAMES
const PRODUCT_CONFIG_JSON_FILE: &str = "/config/data/product_config.json";
const BOARD_CONFIG_JSON_FILE: &str = "/config/data/board_config.json";
const DEFAULT_PRODUCT_CONFIG_JSON_FILE: &str = "/config/data/default_product_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";
async fn read_factory_file(
path: &str,
proxy_handle: &MiscFactoryStoreProviderProxy,
) -> Result<String, Error> {
let (dir_proxy, dir_server_end) = create_proxy::<DirectoryMarker>()?;
proxy_handle.get_factory_store(dir_server_end)?;
let file_proxy = io_util::open_file(&dir_proxy, &Path::new(path), OPEN_RIGHT_READABLE)?;
let result = io_util::read_file(&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) => {
fx_log_err!("Failed to read factory file {}: {}", SERIAL_TXT.to_string(), 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,
..fidl_fuchsia_hwinfo::DeviceInfo::EMPTY
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BoardInfo {
pub name: Option<String>,
pub revision: Option<String>,
}
impl BoardInfo {
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 board_info = BoardInfo::read_config(BOARD_CONFIG_JSON_FILE).unwrap_or_else(|err| {
fx_log_err!("Failed to read board_config.json due to {}", err);
BoardInfo::read_config(DEFAULT_BOARD_CONFIG_JSON_FILE).unwrap_or_else(|err| {
fx_log_err!("Failed to read default_board_config.json due to {}", err);
BoardInfo { name: None, revision: None }
})
});
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,
..fidl_fuchsia_hwinfo::BoardInfo::EMPTY
}
}
}
#[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_config_data(&mut self, path: &str) -> Result<(), Error> {
let config_map: ConfigFile =
serde_json::from_reader(io::BufReader::new(File::open(path)?))?;
self.name = Some(config_map.name);
self.model = Some(config_map.model);
self.manufacturer = Some(config_map.manufacturer);
Ok(())
}
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];
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());
}
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());
}
_ => {
fx_log_warn!("hw.txt dictionary values {} - {}", key, value.to_owned());
}
}
fx_log_warn!("hw.txt line: {}", config);
}
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();
if let Err(err) = product_info.load_from_config_data(PRODUCT_CONFIG_JSON_FILE) {
fx_log_err!("Failed to load product_config.json due to {}", err);
if let Err(err) = product_info.load_from_config_data(DEFAULT_PRODUCT_CONFIG_JSON_FILE) {
fx_log_err!("Failed to load default_product_config.json due to {}", err);
}
}
if let Err(err) = product_info.load_from_hw_file(HW_TXT, proxy_handle).await {
fx_log_err!("Failed to load hw.txt.txt due to {}", err);
}
if let Err(err) = product_info.load_from_locale_list(LOCALE_LIST_FILE, proxy_handle).await {
fx_log_err!("Failed to load local_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,
..RegulatoryDomain::EMPTY
})
},
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,
..fidl_fuchsia_hwinfo::ProductInfo::EMPTY
}
}
}