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