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