1 use crate::Error;
2 use crate::RecordArgs;
3 use crate::StartArgs;
4 use log::info;
5 use log::warn;
6 use std::fs::File;
7 use std::fs::OpenOptions;
8 use std::io::Write;
9 use std::time::Duration;
10
11 use rustutils::system_properties::error::PropertyWatcherError;
12 use rustutils::system_properties::PropertyWatcher;
13
14 const PREFETCH_RECORD_PROPERTY: &str = "prefetch_boot.record";
15 const PREFETCH_REPLAY_PROPERTY: &str = "prefetch_boot.replay";
16 const PREFETCH_RECORD_PROPERTY_STOP: &str = "ro.prefetch_boot.record_stop";
17
wait_for_property_true( property_name: &str, timeout: Option<Duration>, ) -> Result<(), PropertyWatcherError>18 fn wait_for_property_true(
19 property_name: &str,
20 timeout: Option<Duration>,
21 ) -> Result<(), PropertyWatcherError> {
22 let mut prop = PropertyWatcher::new(property_name)?;
23 prop.wait_for_value("1", timeout)?;
24 Ok(())
25 }
26
27 /// Wait for record to stop
wait_for_record_stop()28 pub fn wait_for_record_stop() {
29 wait_for_property_true(PREFETCH_RECORD_PROPERTY_STOP, None).unwrap_or_else(|e| {
30 warn!("failed to wait for {} with error: {}", PREFETCH_RECORD_PROPERTY_STOP, e)
31 });
32 }
33
start_prefetch_service(property_name: &str) -> Result<(), Error>34 fn start_prefetch_service(property_name: &str) -> Result<(), Error> {
35 match rustutils::system_properties::write(property_name, "true") {
36 Ok(_) => {}
37 Err(_) => {
38 return Err(Error::Custom { error: "Failed to start prefetch service".to_string() });
39 }
40 }
41 Ok(())
42 }
43
44 /// Start prefetch service
45 ///
46 /// 1: Check the presence of the file 'prefetch_ready'. If it doesn't
47 /// exist then the device is booting for the first time after wipe.
48 /// Thus, we would just create the file and exit as we do not want
49 /// to initiate the record after data wipe primiarly because boot
50 /// after data wipe is long and the I/O pattern during first boot may not actually match
51 /// with subsequent boot.
52 ///
53 /// 2: If the file 'prefetch_ready' is present:
54 ///
55 /// a: Compare the build-finger-print of the device with the one record format
56 /// is associated with by reading the file 'build_finger_print'. If they match,
57 /// start the prefetch_replay.
58 ///
59 /// b: If they don't match, then the device was updated through OTA. Hence, start
60 /// a fresh record and delete the build-finger-print file. This should also cover
61 /// the case of device rollback.
62 ///
63 /// c: If the build-finger-print file doesn't exist, then just restart the record
64 /// from scratch.
start_prefetch(args: &StartArgs) -> Result<(), Error>65 pub fn start_prefetch(args: &StartArgs) -> Result<(), Error> {
66 if !args.path.exists() {
67 match File::create(args.path.clone()) {
68 Ok(_) => {}
69 Err(_) => {
70 return Err(Error::Custom { error: "File Creation failed".to_string() });
71 }
72 }
73 return Ok(());
74 }
75
76 if args.build_fingerprint_path.exists() {
77 let device_build_fingerprint = rustutils::system_properties::read("ro.build.fingerprint")
78 .map_err(|e| Error::Custom {
79 error: format!("Failed to read ro.build.fingerprint: {}", e),
80 })?;
81 let pack_build_fingerprint = std::fs::read_to_string(&args.build_fingerprint_path)?;
82 if pack_build_fingerprint.trim() == device_build_fingerprint.as_deref().unwrap_or_default()
83 {
84 info!("Start replay");
85 start_prefetch_service(PREFETCH_REPLAY_PROPERTY)?;
86 } else {
87 info!("Start record");
88 std::fs::remove_file(&args.build_fingerprint_path)?;
89 start_prefetch_service(PREFETCH_RECORD_PROPERTY)?;
90 }
91 } else {
92 info!("Start record");
93 start_prefetch_service(PREFETCH_RECORD_PROPERTY)?;
94 }
95 Ok(())
96 }
97
98 /// Write build finger print to associate prefetch pack file
write_build_fingerprint(args: &RecordArgs) -> Result<(), Error>99 pub fn write_build_fingerprint(args: &RecordArgs) -> Result<(), Error> {
100 let mut build_fingerprint_file = OpenOptions::new()
101 .write(true)
102 .create(true)
103 .truncate(true)
104 .open(&args.build_fingerprint_path)
105 .map_err(|source| Error::Create {
106 source,
107 path: args.build_fingerprint_path.to_str().unwrap().to_owned(),
108 })?;
109
110 let device_build_fingerprint =
111 rustutils::system_properties::read("ro.build.fingerprint").unwrap_or_default();
112 let device_build_fingerprint = device_build_fingerprint.unwrap_or_default();
113
114 build_fingerprint_file.write_all(device_build_fingerprint.as_bytes())?;
115 build_fingerprint_file.sync_all()?;
116
117 Ok(())
118 }
119