blob: 4835ab220882e2fee6124492c18b7f5d62adceb6 [file] [log] [blame]
//
// 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