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 policy::DevicePolicy;
36
37 namespace chromeos_update_manager {
38
GetStagingSchedule(const DevicePolicy * device_policy,StagingSchedule * staging_schedule_out)39 int GetStagingSchedule(const DevicePolicy* device_policy,
40 StagingSchedule* staging_schedule_out) {
41 StagingSchedule staging_schedule;
42 if (!device_policy->GetDeviceUpdateStagingSchedule(&staging_schedule) ||
43 staging_schedule.empty()) {
44 return 0;
45 }
46
47 // Last percentage of the schedule should be 100.
48 if (staging_schedule.back().percentage != 100) {
49 LOG(ERROR) << "Last percentage of the schedule is not 100, it's: "
50 << staging_schedule.back().percentage;
51 return 0;
52 }
53
54 int previous_days = 0;
55 int previous_percentage = -1;
56 // Ensure that the schedule has a monotonically increasing set of percentages
57 // and that days are also monotonically increasing.
58 for (const auto& staging_pair : staging_schedule) {
59 int days = staging_pair.days;
60 if (previous_days >= days) {
61 LOG(ERROR) << "Days in staging schedule are not monotonically "
62 << "increasing. Previous value: " << previous_days
63 << " Current value: " << days;
64 return 0;
65 }
66 previous_days = days;
67 int percentage = staging_pair.percentage;
68 if (previous_percentage >= percentage) {
69 LOG(ERROR) << "Percentages in staging schedule are not monotonically "
70 << "increasing. Previous value: " << previous_percentage
71 << " Current value: " << percentage;
72 return 0;
73 }
74 previous_percentage = percentage;
75 }
76 // Modify staging schedule only if the schedule in the device policy is valid.
77 if (staging_schedule_out)
78 *staging_schedule_out = std::move(staging_schedule);
79
80 return previous_days;
81 }
82
CalculateWaitTimeInDaysFromSchedule(const StagingSchedule & staging_schedule)83 int CalculateWaitTimeInDaysFromSchedule(
84 const StagingSchedule& staging_schedule) {
85 int prev_days = 0;
86 int percentage_position = base::RandInt(1, 100);
87 for (const auto& staging_pair : staging_schedule) {
88 int days = staging_pair.days;
89 if (percentage_position <= staging_pair.percentage) {
90 // Scatter between the start of the range and the end.
91 return prev_days + base::RandInt(1, days - prev_days);
92 }
93 prev_days = days;
94 }
95 // Something went wrong.
96 NOTREACHED();
97 return 0;
98 }
99
CalculateStagingCase(const DevicePolicy * device_policy,PrefsInterface * prefs,TimeDelta * staging_wait_time,StagingSchedule * staging_schedule)100 StagingCase CalculateStagingCase(const DevicePolicy* device_policy,
101 PrefsInterface* prefs,
102 TimeDelta* staging_wait_time,
103 StagingSchedule* staging_schedule) {
104 // Check that the schedule in the device policy is correct.
105 StagingSchedule new_staging_schedule;
106 int max_days = GetStagingSchedule(device_policy, &new_staging_schedule);
107 if (max_days == 0)
108 return StagingCase::kOff;
109
110 // Calculate the new wait time.
111 TimeDelta new_staging_wait_time = TimeDelta::FromDays(
112 CalculateWaitTimeInDaysFromSchedule(new_staging_schedule));
113 DCHECK_GT(new_staging_wait_time.InSeconds(), 0);
114 if (staging_wait_time->InSeconds() > 0) {
115 // If there hasn't been any changes to the schedule and there is a value
116 // set, don't change the waiting time.
117 if (new_staging_schedule == *staging_schedule) {
118 return StagingCase::kNoAction;
119 }
120 // Otherwise, update the schedule and wait time.
121 *staging_wait_time = new_staging_wait_time;
122 *staging_schedule = std::move(new_staging_schedule);
123 return StagingCase::kNoSavedValue;
124 }
125 // Getting this means the schedule changed, update the old schedule.
126 *staging_schedule = std::move(new_staging_schedule);
127
128 int64_t wait_period_in_days;
129 // There exists a persisted value that is valid. That is, it's smaller than
130 // the maximum amount of days of staging set by the user.
131 if (prefs->GetInt64(kPrefsWallClockStagingWaitPeriod, &wait_period_in_days) &&
132 wait_period_in_days > 0 && wait_period_in_days <= max_days) {
133 *staging_wait_time = TimeDelta::FromDays(wait_period_in_days);
134 return StagingCase::kSetStagingFromPref;
135 }
136
137 *staging_wait_time = new_staging_wait_time;
138 return StagingCase::kNoSavedValue;
139 }
140
141 } // namespace chromeos_update_manager
142