1 //
2 // Copyright (C) 2018 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/update_manager/staging_utils.h"
18
19 #include <utility>
20 #include <vector>
21
22 #include <base/logging.h>
23 #include <base/rand_util.h>
24 #include <base/time/time.h>
25 #include <policy/device_policy.h>
26
27 #include "update_engine/common/constants.h"
28 #include "update_engine/common/hardware_interface.h"
29 #include "update_engine/common/prefs_interface.h"
30 #include "update_engine/system_state.h"
31
32 using base::TimeDelta;
33 using chromeos_update_engine::kPrefsWallClockStagingWaitPeriod;
34 using chromeos_update_engine::PrefsInterface;
35 using chromeos_update_engine::SystemState;
36 using policy::DevicePolicy;
37
38 namespace chromeos_update_manager {
39
GetStagingSchedule(const DevicePolicy * device_policy,StagingSchedule * staging_schedule_out)40 int GetStagingSchedule(const DevicePolicy* device_policy,
41 StagingSchedule* staging_schedule_out) {
42 StagingSchedule staging_schedule;
43 if (!device_policy->GetDeviceUpdateStagingSchedule(&staging_schedule) ||
44 staging_schedule.empty()) {
45 return 0;
46 }
47
48 // Last percentage of the schedule should be 100.
49 if (staging_schedule.back().percentage != 100) {
50 LOG(ERROR) << "Last percentage of the schedule is not 100, it's: "
51 << staging_schedule.back().percentage;
52 return 0;
53 }
54
55 int previous_days = 0;
56 int previous_percentage = -1;
57 // Ensure that the schedule has a monotonically increasing set of percentages
58 // and that days are also monotonically increasing.
59 for (const auto& staging_pair : staging_schedule) {
60 int days = staging_pair.days;
61 if (previous_days >= days) {
62 LOG(ERROR) << "Days in staging schedule are not monotonically "
63 << "increasing. Previous value: " << previous_days
64 << " Current value: " << days;
65 return 0;
66 }
67 previous_days = days;
68 int percentage = staging_pair.percentage;
69 if (previous_percentage >= percentage) {
70 LOG(ERROR) << "Percentages in staging schedule are not monotonically "
71 << "increasing. Previous value: " << previous_percentage
72 << " Current value: " << percentage;
73 return 0;
74 }
75 previous_percentage = percentage;
76 }
77 // Modify staging schedule only if the schedule in the device policy is valid.
78 if (staging_schedule_out)
79 *staging_schedule_out = std::move(staging_schedule);
80
81 return previous_days;
82 }
83
CalculateWaitTimeInDaysFromSchedule(const StagingSchedule & staging_schedule)84 int CalculateWaitTimeInDaysFromSchedule(
85 const StagingSchedule& staging_schedule) {
86 int prev_days = 0;
87 int percentage_position = base::RandInt(1, 100);
88 for (const auto& staging_pair : staging_schedule) {
89 int days = staging_pair.days;
90 if (percentage_position <= staging_pair.percentage) {
91 // Scatter between the start of the range and the end.
92 return prev_days + base::RandInt(1, days - prev_days);
93 }
94 prev_days = days;
95 }
96 // Something went wrong.
97 NOTREACHED();
98 return 0;
99 }
100
CalculateStagingCase(const DevicePolicy * device_policy,PrefsInterface * prefs,TimeDelta * staging_wait_time,StagingSchedule * staging_schedule)101 StagingCase CalculateStagingCase(const DevicePolicy* device_policy,
102 PrefsInterface* prefs,
103 TimeDelta* staging_wait_time,
104 StagingSchedule* staging_schedule) {
105 // Check that the schedule in the device policy is correct.
106 StagingSchedule new_staging_schedule;
107 int max_days = GetStagingSchedule(device_policy, &new_staging_schedule);
108 if (max_days == 0)
109 return StagingCase::kOff;
110
111 // Calculate the new wait time.
112 TimeDelta new_staging_wait_time = TimeDelta::FromDays(
113 CalculateWaitTimeInDaysFromSchedule(new_staging_schedule));
114 DCHECK_GT(new_staging_wait_time.InSeconds(), 0);
115 if (staging_wait_time->InSeconds() > 0) {
116 // If there hasn't been any changes to the schedule and there is a value
117 // set, don't change the waiting time.
118 if (new_staging_schedule == *staging_schedule) {
119 return StagingCase::kNoAction;
120 }
121 // Otherwise, update the schedule and wait time.
122 *staging_wait_time = new_staging_wait_time;
123 *staging_schedule = std::move(new_staging_schedule);
124 return StagingCase::kNoSavedValue;
125 }
126 // Getting this means the schedule changed, update the old schedule.
127 *staging_schedule = std::move(new_staging_schedule);
128
129 int64_t wait_period_in_days;
130 // There exists a persisted value that is valid. That is, it's smaller than
131 // the maximum amount of days of staging set by the user.
132 if (prefs->GetInt64(kPrefsWallClockStagingWaitPeriod, &wait_period_in_days) &&
133 wait_period_in_days > 0 && wait_period_in_days <= max_days) {
134 *staging_wait_time = TimeDelta::FromDays(wait_period_in_days);
135 return StagingCase::kSetStagingFromPref;
136 }
137
138 *staging_wait_time = new_staging_wait_time;
139 return StagingCase::kNoSavedValue;
140 }
141
142 } // namespace chromeos_update_manager
143