omaha_client/
policy.rs

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.
8
9use crate::{
10    common::{App, CheckOptions, CheckTiming, ProtocolState, UpdateCheckSchedule},
11    installer::Plan,
12    request_builder::RequestParams,
13    time::{ComplexTime, TimeSource},
14};
15use futures::future::BoxFuture;
16
17#[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;
25
26/// 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))]
31    pub current_time: ComplexTime,
32}
33
34/// Reasons why a check can/cannot be performed at this time
35#[derive(Clone, Debug, PartialEq)]
36pub enum CheckDecision {
37    /// positive responses
38    Ok(RequestParams),
39    /// but with caveats:
40    OkUpdateDeferred(RequestParams),
41
42    /// negative responses
43    TooSoon,
44    ThrottledByPolicy,
45    DeniedByPolicy,
46}
47
48#[cfg(test)]
49impl Default for CheckDecision {
50    fn default() -> Self {
51        CheckDecision::Ok(RequestParams::default())
52    }
53}
54
55/// Reasons why an update can/cannot be performed at this time
56#[derive(Clone, Debug, PartialEq)]
57pub enum UpdateDecision {
58    /// Update can be performed.
59    Ok,
60    /// Update is deferred by Policy.
61    DeferredByPolicy,
62    /// Update is rejected by Policy.
63    DeniedByPolicy,
64}
65
66#[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 {
71    fn default() -> Self {
72        UpdateDecision::Ok
73    }
74}
75
76/// The policy implementation itself
77pub trait Policy {
78    type ComputeNextUpdateTimePolicyData;
79    type UpdateCheckAllowedPolicyData;
80    type UpdateCanStartPolicyData;
81    type RebootPolicyData;
82    type InstallPlan: Plan;
83
84    /// When should the next update happen?
85    fn compute_next_update_time(
86        policy_data: &Self::ComputeNextUpdateTimePolicyData,
87        apps: &[App],
88        scheduling: &UpdateCheckSchedule,
89        protocol_state: &ProtocolState,
90    ) -> CheckTiming;
91
92    /// 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.
97    fn update_check_allowed(
98        policy_data: &Self::UpdateCheckAllowedPolicyData,
99        apps: &[App],
100        scheduling: &UpdateCheckSchedule,
101        protocol_state: &ProtocolState,
102        check_options: &CheckOptions,
103    ) -> CheckDecision;
104
105    /// Given the current State, the current PolicyData, can the proposed InstallPlan
106    /// be executed at this time.
107    fn update_can_start(
108        policy_data: &Self::UpdateCanStartPolicyData,
109        proposed_install_plan: &Self::InstallPlan,
110    ) -> UpdateDecision;
111
112    /// Given the current PolicyData, is reboot allowed right now.
113    fn reboot_allowed(policy_data: &Self::RebootPolicyData, check_options: &CheckOptions) -> bool;
114
115    /// Given the InstallPlan, is reboot needed after update has been installed.
116    fn reboot_needed(install_plan: &Self::InstallPlan) -> bool;
117}
118
119pub trait PolicyEngine {
120    type TimeSource: TimeSource + Clone;
121    type InstallResult;
122    type InstallPlan: Plan;
123
124    /// Provides the time source used by the PolicyEngine to the state machine.
125    fn time_source(&self) -> &Self::TimeSource;
126
127    /// When should the next update happen?
128    fn 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>;
134
135    /// 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.
139    fn 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>;
146
147    /// Given the current State, the current PolicyData, can the proposed InstallPlan
148    /// be executed at this time.
149    fn update_can_start<'a>(
150        &'a mut self,
151        proposed_install_plan: &'a Self::InstallPlan,
152    ) -> BoxFuture<'a, UpdateDecision>;
153
154    /// Is reboot allowed right now.
155    fn reboot_allowed<'a>(
156        &'a mut self,
157        check_options: &'a CheckOptions,
158        install_result: &'a Self::InstallResult,
159    ) -> BoxFuture<'a, bool>;
160
161    /// Given the InstallPlan, is reboot needed after update has been installed.
162    fn reboot_needed<'a>(&'a mut self, install_plan: &'a Self::InstallPlan) -> BoxFuture<'a, bool>;
163}
164
165#[cfg(test)]
166mod test {
167    use super::*;
168    use crate::time::MockTimeSource;
169
170    #[test]
171    pub fn test_policy_data_builder_with_system_time() {
172        let current_time = MockTimeSource::new_from_now().now();
173        let policy_data = PolicyData::builder().current_time(current_time).build();
174        assert_eq!(policy_data.current_time, current_time);
175    }
176
177    #[test]
178    pub fn test_policy_data_builder_with_clock() {
179        let source = MockTimeSource::new_from_now();
180        let current_time = source.now();
181        let policy_data = PolicyData::builder().current_time(source.now()).build();
182        assert_eq!(policy_data.current_time, current_time);
183    }
184}