omaha_client/policy.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
// Copyright 2019 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use crate::{
common::{App, CheckOptions, CheckTiming, ProtocolState, UpdateCheckSchedule},
installer::Plan,
request_builder::RequestParams,
time::{ComplexTime, TimeSource},
};
use futures::future::BoxFuture;
#[cfg(test)]
mod mock;
#[cfg(test)]
pub use mock::MockPolicyEngine;
mod stub;
pub use stub::StubPolicy;
pub use stub::StubPolicyEngine;
use typed_builder::TypedBuilder;
/// Data about the local system that's needed to fulfill Policy questions
#[derive(Clone, Debug, TypedBuilder)]
pub struct PolicyData {
/// The current time at the start of the update
#[builder(setter(into))]
pub current_time: ComplexTime,
}
/// Reasons why a check can/cannot be performed at this time
#[derive(Clone, Debug, PartialEq)]
pub enum CheckDecision {
/// positive responses
Ok(RequestParams),
/// but with caveats:
OkUpdateDeferred(RequestParams),
/// negative responses
TooSoon,
ThrottledByPolicy,
DeniedByPolicy,
}
#[cfg(test)]
impl Default for CheckDecision {
fn default() -> Self {
CheckDecision::Ok(RequestParams::default())
}
}
/// Reasons why an update can/cannot be performed at this time
#[derive(Clone, Debug, PartialEq)]
pub enum UpdateDecision {
/// Update can be performed.
Ok,
/// Update is deferred by Policy.
DeferredByPolicy,
/// Update is rejected by Policy.
DeniedByPolicy,
}
#[cfg(test)]
// Disable clippy lint for this impl as we want to derive it manually so that it is only available
// for tests.
#[allow(clippy::derivable_impls)]
impl Default for UpdateDecision {
fn default() -> Self {
UpdateDecision::Ok
}
}
/// The policy implementation itself
pub trait Policy {
type ComputeNextUpdateTimePolicyData;
type UpdateCheckAllowedPolicyData;
type UpdateCanStartPolicyData;
type RebootPolicyData;
type InstallPlan: Plan;
/// When should the next update happen?
fn compute_next_update_time(
policy_data: &Self::ComputeNextUpdateTimePolicyData,
apps: &[App],
scheduling: &UpdateCheckSchedule,
protocol_state: &ProtocolState,
) -> CheckTiming;
/// Given the current State, and the current PolicyData, is an update check
/// allowed at this time. A CheckDecision is used to return the reasoning, as in
/// some cases, instead of an update check, the SM will instead notify Omaha that
/// it would perform an update, but instead just tell the device whether or not
/// an update is available.
fn update_check_allowed(
policy_data: &Self::UpdateCheckAllowedPolicyData,
apps: &[App],
scheduling: &UpdateCheckSchedule,
protocol_state: &ProtocolState,
check_options: &CheckOptions,
) -> CheckDecision;
/// Given the current State, the current PolicyData, can the proposed InstallPlan
/// be executed at this time.
fn update_can_start(
policy_data: &Self::UpdateCanStartPolicyData,
proposed_install_plan: &Self::InstallPlan,
) -> UpdateDecision;
/// Given the current PolicyData, is reboot allowed right now.
fn reboot_allowed(policy_data: &Self::RebootPolicyData, check_options: &CheckOptions) -> bool;
/// Given the InstallPlan, is reboot needed after update has been installed.
fn reboot_needed(install_plan: &Self::InstallPlan) -> bool;
}
pub trait PolicyEngine {
type TimeSource: TimeSource + Clone;
type InstallResult;
type InstallPlan: Plan;
/// Provides the time source used by the PolicyEngine to the state machine.
fn time_source(&self) -> &Self::TimeSource;
/// When should the next update happen?
fn compute_next_update_time<'a>(
&'a mut self,
apps: &'a [App],
scheduling: &'a UpdateCheckSchedule,
protocol_state: &'a ProtocolState,
) -> BoxFuture<'a, CheckTiming>;
/// Given the context provided by State, does the Policy allow an update check to
/// happen at this time? This should be consistent with the compute_next_update_time
/// so that during background updates, the result of compute_next_update_time will
/// result in a CheckDecision::Ok() value from this function.
fn update_check_allowed<'a>(
&'a mut self,
apps: &'a [App],
scheduling: &'a UpdateCheckSchedule,
protocol_state: &'a ProtocolState,
check_options: &'a CheckOptions,
) -> BoxFuture<'a, CheckDecision>;
/// Given the current State, the current PolicyData, can the proposed InstallPlan
/// be executed at this time.
fn update_can_start<'a>(
&'a mut self,
proposed_install_plan: &'a Self::InstallPlan,
) -> BoxFuture<'a, UpdateDecision>;
/// Is reboot allowed right now.
fn reboot_allowed<'a>(
&'a mut self,
check_options: &'a CheckOptions,
install_result: &'a Self::InstallResult,
) -> BoxFuture<'a, bool>;
/// Given the InstallPlan, is reboot needed after update has been installed.
fn reboot_needed<'a>(&'a mut self, install_plan: &'a Self::InstallPlan) -> BoxFuture<'a, bool>;
}
#[cfg(test)]
mod test {
use super::*;
use crate::time::MockTimeSource;
#[test]
pub fn test_policy_data_builder_with_system_time() {
let current_time = MockTimeSource::new_from_now().now();
let policy_data = PolicyData::builder().current_time(current_time).build();
assert_eq!(policy_data.current_time, current_time);
}
#[test]
pub fn test_policy_data_builder_with_clock() {
let source = MockTimeSource::new_from_now();
let current_time = source.now();
let policy_data = PolicyData::builder().current_time(source.now()).build();
assert_eq!(policy_data.current_time, current_time);
}
}