//
|
// 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
|