• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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