1use anyhow::Error;
6use async_lock::OnceCell;
7use fidl_fuchsia_feedback::{LastRebootInfoProviderMarker, RebootReason};
8use fidl_fuchsia_io as fio;
9use fuchsia_component::client::connect_to_protocol_sync;
10use fuchsia_fs::node::OpenError;
11use log::{debug, info};
12use zx_status::Status;
13
14const STARTED_ONCE: &str = "component-started-once";
16
17const LRIP_FIDL_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::INFINITE;
19static ANDROID_BOOTREASON: OnceCell<Result<String, Error>> = OnceCell::new();
20
21pub async fn get_or_init_android_bootreason(
23 dir: Option<fio::DirectoryProxy>,
24 android_provided_bootreason: Option<String>,
25) -> &'static Result<String, Error> {
26 ANDROID_BOOTREASON
27 .get_or_init(async || update_android_bootreason(dir, android_provided_bootreason).await)
28 .await
29}
30
31pub async fn update_android_bootreason(
36 dir: Option<fio::DirectoryProxy>,
37 android_provided_bootreason: Option<String>,
38) -> Result<String, Error> {
39 if let Some(dir) = dir {
40 match fuchsia_fs::directory::open_file(&dir, STARTED_ONCE, fio::Flags::FLAG_MUST_CREATE)
41 .await
42 {
43 Ok(_file) => (),
44 Err(OpenError::OpenError(Status::ALREADY_EXISTS)) => {
45 info!("Session restart observed, set android bootreason to kernel_panic.");
46 return Ok("kernel_panic".to_string());
47 }
48 Err(err) => {
49 info!(
50 "Failed to generate the file with err {err:#?}. Continue with LastRebootInfo."
51 );
52 }
53 }
54 }
55
56 if let Some(reason) = &android_provided_bootreason {
59 if reason.starts_with("reboot,uvlo") || reason.starts_with("reboot,longkey") {
60 return Ok(reason.clone());
61 }
62 }
63
64 info!("Converting LastRebootInfo to an android-friendly bootreason.");
65 let reboot_info_proxy = connect_to_protocol_sync::<LastRebootInfoProviderMarker>()?;
66 let deadline = zx::MonotonicInstant::after(LRIP_FIDL_TIMEOUT);
67 let reboot_info = reboot_info_proxy.get(deadline)?;
68
69 let bootreason = match reboot_info.reason {
70 Some(RebootReason::Unknown) => "reboot,unknown",
71 Some(RebootReason::Cold) => "reboot,cold",
72 Some(RebootReason::BriefPowerLoss) => "reboot,hard_reset",
73 Some(RebootReason::Brownout) => "reboot,undervoltage",
74 Some(RebootReason::KernelPanic) => "kernel_panic",
75 Some(RebootReason::SystemOutOfMemory) => "kernel_panic,oom",
76 Some(RebootReason::HardwareWatchdogTimeout) => "watchdog",
77 Some(RebootReason::SoftwareWatchdogTimeout) => "watchdog,sw",
78 Some(RebootReason::RootJobTermination) => "kernel_panic",
79 Some(RebootReason::UserRequest) => "reboot,userrequested",
80 Some(RebootReason::UserRequestDeviceStuck) => "reboot,userrequested",
81 Some(RebootReason::DeveloperRequest) => "reboot,shell",
82 Some(RebootReason::RetrySystemUpdate) => "reboot,ota",
83 Some(RebootReason::HighTemperature) => "shutdown,thermal",
84 Some(RebootReason::SessionFailure) => "kernel_panic",
85 Some(RebootReason::SysmgrFailure) => "kernel_panic",
86 Some(RebootReason::FactoryDataReset) => "reboot,factory_reset",
87 Some(RebootReason::CriticalComponentFailure) => "kernel_panic",
88 Some(RebootReason::ZbiSwap) => "reboot,normal",
89 Some(RebootReason::SystemUpdate) => "reboot,ota",
90 Some(RebootReason::NetstackMigration) => "reboot,normal",
91 Some(RebootReason::AndroidUnexpectedReason) => "reboot,normal",
92 Some(RebootReason::AndroidNoReason) => "reboot",
93 Some(RebootReason::AndroidRescueParty) => "reboot,rescueparty",
94 Some(RebootReason::AndroidCriticalProcessFailure) => "reboot,userspace_failed",
95 Some(RebootReason::BatteryDrained) => "shutdown,battery",
96 Some(RebootReason::__SourceBreaking { .. }) => "reboot,normal",
97 None => "reboot,unknown",
98 };
99 Ok(bootreason.to_string())
100}
101
102fn get_reboot_reason() -> Option<RebootReason> {
104 let reboot_info_proxy = connect_to_protocol_sync::<LastRebootInfoProviderMarker>().ok();
105 let deadline = zx::MonotonicInstant::after(LRIP_FIDL_TIMEOUT);
106 let reboot_info = reboot_info_proxy?.get(deadline);
107 match reboot_info {
108 Ok(info) => match info.reason {
109 Some(r) => Some(r),
110 None => {
111 info!("Failed to get the reboot reason.");
112 Some(RebootReason::unknown())
113 }
114 },
115 Err(e) => {
116 info!("Failed to get the reboot info: {:?}", e);
117 Some(RebootReason::unknown())
118 }
119 }
120}
121
122pub fn get_console_ramoops() -> Option<Vec<u8>> {
127 debug!("Getting console-ramoops contents");
128 match ANDROID_BOOTREASON.wait_blocking() {
129 Ok(reason) => match reason.as_str() {
130 "kernel_panic" | "watchdog" | "watchdog,sw" => Some(
131 format!("Last Reboot Reason: {:?}\n", get_reboot_reason()?).as_bytes().to_vec(),
132 ),
133 _ => None,
134 },
135 Err(e) => {
136 info!("Failed to get android bootreason for console_ramoops: {:?}", e);
137 None
138 }
139 }
140}