/// Updates the system in the given `Environment` using the provided config options.
///
/// Reboot vs RebootDeferred behavior is determined in the following priority order:
/// * is mode ForceRecovery? If so, reboot.
/// * is there a reboot controller? If so, yield reboot to the controller.
/// * if none of the above are true, reboot depending on the value of `Config::should_reboot`.
///
/// If a reboot is deferred, the initiator of the update is responsible for triggering
/// the reboot.
Updates the system in the given `Environment` using the provided config options.
Reboot vs RebootDeferred behavior is determined in the following priority order:
is mode ForceRecovery. If so, reboot.
is there a reboot controller. If so, yield reboot to the controller.
if none of the above are true, reboot depending on the value of `Config::should_reboot`.

If a reboot is deferred, the initiator of the update is responsible for triggering
the reboot.
Updates the system in the given ` Environment ` using the provided config options . Reboot vs RebootDeferred behavior is determined in the following priority order : is mode ForceRecovery . If so reboot . is there a reboot controller . If so yield reboot to the controller . if none of the above are true reboot depending on the value of ` Config :: should_reboot ` . If a reboot is deferred the initiator of the update is responsible for triggering the reboot .
async fn update(
    config: Config,
    env: Environment,
    history: Arc<Mutex<UpdateHistory>>,
    reboot_controller: RebootController,
) -> (String, impl FusedStream<Item = State>) {
    let attempt_fut = history.lock().start_update_attempt(
        Options {
            initiator: config.initiator.into(),
            allow_attach_to_existing_attempt: config.allow_attach_to_existing_attempt,
            should_write_recovery: config.should_write_recovery,
        },
        config.update_url.clone(),
        config.start_time,
        &env.data_sink,
        &env.boot_manager,
        &env.build_info,
        &env.pkgfs_system,
    );
    let attempt = attempt_fut.await;
    let source_version = attempt.source_version().clone();
    let power_state_control = env.power_state_control.clone();
    let history_clone = Arc::clone(&history);
    let attempt_id = attempt.attempt_id().to_string();
    let stream = async_generator::generate(move |mut co| async move {
        let history = history_clone;
        // The only operation allowed to fail in this function is update_attempt. The rest of the
        // functionality here sets up the update attempt and takes the appropriate actions based on
        // whether the update attempt succeeds or fails.
        let mut phase = metrics::Phase::Tufupdate;
        let (mut cobalt, cobalt_forwarder_task) = env.cobalt_connector.connect();
        let cobalt_forwarder_task = Task::spawn(cobalt_forwarder_task);
        fx_log_info!("starting system update with config: {:?}", config);
        cobalt.log_ota_start("", config.initiator, config.start_time);
        let mut target_version = history::Version::default();
        let attempt_res = Attempt { config: &config, env: &env }
            .run(&mut co, &mut phase, &mut target_version)
            .await;
        fx_log_info!("system update attempt completed, logging metrics");
        let status_code = metrics::result_to_status_code(attempt_res.as_ref().map(|_| ()));
        let target_build_version = target_version.build_version.to_string();
        cobalt.log_ota_result_attempt(
            &target_build_version,
            config.initiator,
            history.lock().attempts_for(&source_version, &target_version) + 1,
            phase,
            status_code,
        );
        cobalt.log_ota_result_duration(
            &target_build_version,
            config.initiator,
            phase,
            status_code,
            config.start_time_mono.elapsed(),
        );
        drop(cobalt);
        if attempt_res.is_ok() {
            channel::update_current_channel().await;
        }
        // wait for all cobalt events to be flushed to the service.
        fx_log_info!("flushing cobalt events");
        let () = flush_cobalt(cobalt_forwarder_task, COBALT_FLUSH_TIMEOUT).await;
        let (state, mode, _packages) = match attempt_res {
            Ok(ok) => ok,
            Err(e) => {
                fx_log_err!("system update failed: {:#}", anyhow!(e));
                return target_version;
            }
        };
        fx_log_info!("checking if reboot is required or should be deferred, mode: {:?}", mode);
        // Figure out if we should reboot.
        match mode {
            // First priority: Always reboot on ForceRecovery success, even if the caller
            // asked to defer the reboot.
            UpdateMode::ForceRecovery => {
                fx_log_info!("system update in ForceRecovery mode complete, rebooting...");
            }
            // Second priority: Use the attached reboot controller.
            UpdateMode::Normal => {
                fx_log_info!("system update complete, waiting for initiator to signal reboot.");
                match reboot_controller.wait_to_reboot().await {
                    CommitAction::Reboot => {
                        fx_log_info!("initiator ready to reboot, rebooting...");
                    }
                    CommitAction::RebootDeferred => {
                        fx_log_info!("initiator deferred reboot to caller.");
                        state.enter_defer_reboot(&mut co).await;
                        return target_version;
                    }
                }
            }
        }
        state.enter_reboot(&mut co).await;
        target_version
    })
    .when_done(move |last_state: Option<State>, target_version| async move {
        let last_state = last_state.unwrap_or(State::Prepare);
        let should_reboot = matches!(last_state, State::Reboot { .. });
        let attempt = attempt.finish(target_version, last_state);
        history.lock().record_update_attempt(attempt);
        let save_fut = history.lock().save();
        save_fut.await;
        if should_reboot {
            reboot::reboot(&power_state_control).await;
        }
    });
    (attempt_id, stream)
}
async fn update ( config : Config , env : Environment , history : Arc < Mutex < UpdateHistory > > , reboot_controller : RebootController , ) -> ( String , impl FusedStream < Item = State > ) { let attempt_fut = history . lock ( ) . start_update_attempt ( Options { initiator : config . initiator . into ( ) , allow_attach_to_existing_attempt : config . allow_attach_to_existing_attempt , should_write_recovery : config . should_write_recovery , } , config . update_url . clone ( ) , config . start_time , & env . data_sink , & env . boot_manager , & env . build_info , & env . pkgfs_system , ) ; let attempt = attempt_fut . await ; let source_version = attempt . source_version ( ) . clone ( ) ; let power_state_control = env . power_state_control . clone ( ) ; let history_clone = Arc :: clone ( & history ) ; let attempt_id = attempt . attempt_id ( ) . to_string ( ) ; let stream = async_generator :: generate ( move | mut co | async move { let history = history_clone ; // The only operation allowed to fail in this function is update_attempt. The rest of the // functionality here sets up the update attempt and takes the appropriate actions based on // whether the update attempt succeeds or fails. let mut phase = metrics :: Phase :: Tufupdate ; let ( mut cobalt , cobalt_forwarder_task ) = env . cobalt_connector . connect ( ) ; let cobalt_forwarder_task = Task :: spawn ( cobalt_forwarder_task ) ; fx_log_info ! ( " " config ) ; cobalt . log_ota_start ( " " , config . initiator , config . start_time ) ; let mut target_version = history :: Version :: default ( ) ; let attempt_res = Attempt { config : & config , env : & env } . run ( & mut co , & mut phase , & mut target_version ) . await ; fx_log_info ! ( " " ) ; let status_code = metrics :: result_to_status_code ( attempt_res . as_ref ( ) . map ( | _ | ( ) ) ) ; let target_build_version = target_version . build_version . to_string ( ) ; cobalt . log_ota_result_attempt ( & target_build_version , config . initiator , history . lock ( ) . attempts_for ( & source_version , & target_version ) + 1 , phase , status_code , ) ; cobalt . log_ota_result_duration ( & target_build_version , config . initiator , phase , status_code , config . start_time_mono . elapsed ( ) , ) ; drop ( cobalt ) ; if attempt_res . is_ok ( ) { channel :: update_current_channel ( ) . await ; } // wait for all cobalt events to be flushed to the service. fx_log_info ! ( " " ) ; let ( ) = flush_cobalt ( cobalt_forwarder_task , COBALT_FLUSH_TIMEOUT ) . await ; let ( state , mode , _packages ) = match attempt_res { Ok ( ok ) => ok , Err ( e ) => { fx_log_err ! ( " " anyhow ( e ) ) ; return target_version ; } } ; fx_log_info ! ( " " mode ) ; // Figure out if we should reboot. match mode { // First priority: Always reboot on ForceRecovery success, even if the caller // asked to defer the reboot. UpdateMode :: ForceRecovery => { fx_log_info ! ( " " ) ; } // Second priority: Use the attached reboot controller. UpdateMode :: Normal => { fx_log_info ! ( " " ) ; match reboot_controller . wait_to_reboot ( ) . await { CommitAction :: Reboot => { fx_log_info ! ( " " ) ; } CommitAction :: RebootDeferred => { fx_log_info ! ( " " ) ; state . enter_defer_reboot ( & mut co ) . await ; return target_version ; } } } } state . enter_reboot ( & mut co ) . await ; target_version } ) . when_done ( move | last_state : Option < State > , target_version | async move { let last_state = last_state . unwrap_or ( State :: Prepare ) ; let should_reboot = matches ! ( last_state State Reboot { } ) ; let attempt = attempt . finish ( target_version , last_state ) ; history . lock ( ) . record_update_attempt ( attempt ) ; let save_fut = history . lock ( ) . save ( ) ; save_fut . await ; if should_reboot { reboot :: reboot ( & power_state_control ) . await ; } } ) ; ( attempt_id , stream ) }
Updates the system in the given `Environment` using the provided config options.
Updates the system in the given ` Environment ` using the provided config options .
config
Config
env
Environment
history
Arc<Mutex<UpdateHistory>>
reboot_controller
RebootController