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