blob: a2bd247d3f96d4ec14e611294933a9d41f254e4f [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shill/cellular/power_opt.h"
#include <base/time/time.h>
#include "shill/logging.h"
namespace shill {
PowerOpt::PowerOpt(Manager* manager) : manager_(manager) {
current_opt_info_ = nullptr;
}
void PowerOpt::NotifyConnectionFailInvalidApn(const std::string& iccid) {
if (opt_info_.count(iccid) == 0)
return;
PowerOptimizationInfo& info = opt_info_[iccid];
if (!info.last_connect_fail_invalid_apn_time.is_null()) {
info.no_service_invalid_apn_duration +=
base::Time::Now() - info.last_connect_fail_invalid_apn_time;
LOG(INFO) << __func__ << ": "
<< "no_service_invalid_apn_duration (minutes): "
<< info.no_service_invalid_apn_duration.InMinutes();
}
info.last_connect_fail_invalid_apn_time = base::Time::Now();
if (info.no_service_invalid_apn_duration >
kNoServiceInvalidApnTimeThreshold &&
(base::Time::Now() - device_last_online_time_) >
kLastOnlineShortThreshold) {
PerformPowerOptimization(PowerEvent::kInvalidApn);
}
}
void PowerOpt::NotifyRegistrationSuccess(const std::string& iccid) {
if (opt_info_.count(iccid) == 0)
return;
opt_info_[iccid].no_service_invalid_apn_duration = base::Seconds(0);
opt_info_[iccid].last_connect_fail_invalid_apn_time = base::Time();
LOG(INFO) << __func__ << ": Reset invalid Apn related power opt info.";
}
void PowerOpt::UpdateDurationSinceLastOnline(const base::Time& last_online_time,
bool is_user_request) {
if (!current_opt_info_)
return;
base::TimeDelta device_since_last_online;
current_opt_info_->last_online_time = last_online_time;
if (device_last_online_time_.is_null() ||
last_online_time > device_last_online_time_) {
device_last_online_time_ = last_online_time;
}
device_since_last_online = base::Time::Now() - device_last_online_time_;
if (is_user_request && device_since_last_online > kLastOnlineLongThreshold) {
// |last_online|------------|now|----<grace period>---|trigger point|
device_since_last_online =
kLastOnlineLongThreshold - kUserRequestGracePeriod;
}
LOG(INFO) << "Time since device was last online through cellular (minutes): "
<< device_since_last_online.InMinutes();
if (device_since_last_online > kLastOnlineLongThreshold) {
PerformPowerOptimization(PowerEvent::kLongNotOnline);
}
}
bool PowerOpt::UpdatePowerState(const std::string& iccid, PowerState state) {
if (opt_info_.count(iccid) == 0)
return false;
current_opt_info_ = &opt_info_[iccid];
if (state != opt_info_[iccid].power_state) {
opt_info_[iccid].power_state = state;
return true;
}
return false;
}
base::Time PowerOpt::GetLastOnlineTime(const std::string& iccid) {
return opt_info_.count(iccid) > 0 ? opt_info_[iccid].last_online_time
: base::Time();
}
base::TimeDelta PowerOpt::GetInvalidApnDuration(const std::string& iccid) {
return opt_info_.count(iccid) > 0
? opt_info_[iccid].no_service_invalid_apn_duration
: base::Seconds(0);
}
PowerOpt::PowerState PowerOpt::GetPowerState(const std::string& iccid) {
if (opt_info_.count(iccid) > 0)
return opt_info_[iccid].power_state;
else
return PowerState::kUnknown;
}
bool PowerOpt::RequestPowerStateChange(PowerOpt::PowerState power_state) {
if (power_state == PowerState::kLow || power_state == PowerState::kOff) {
LOG(INFO) << __func__ << ": disable cellular.";
manager_->SetEnabledStateForTechnology(kTypeCellular, false, false,
base::DoNothing());
}
return true;
}
bool PowerOpt::AddOptInfoForNewService(const std::string& iccid) {
if (opt_info_.count(iccid) > 0)
return false;
// either |opt_info_| is empty or no match
PowerOptimizationInfo info;
info.power_state = PowerState::kOn;
opt_info_[iccid] = info;
LOG(INFO) << __func__ << ": power optimization info is added for iccid "
<< iccid << ". opt_info_.size() is " << opt_info_.size();
return true;
}
PowerOpt::PowerState PowerOpt::PerformPowerOptimization(
PowerOpt::PowerEvent event) {
Metrics::CellularPowerOptimizationInfo metrics_info;
PowerState target_power_state = PowerState::kUnknown;
switch (event) {
case PowerEvent::kInvalidApn:
if (current_opt_info_->power_state == PowerState::kOn) {
target_power_state = PowerState::kLow;
metrics_info.reason = Metrics::CellularPowerOptimizationInfo::
CellularPowerOptimizationReason::kNoServiceInvalidApn;
metrics_info.new_power_state =
Metrics::CellularPowerOptimizationInfo::PowerState::kLow;
metrics_info.since_last_online_hours =
(base::Time::Now() - device_last_online_time_).InHours();
}
break;
case PowerEvent::kNoRoamingAgreement:
case PowerEvent::kServiceBlocked:
break;
case PowerEvent::kLongNotOnline:
if (current_opt_info_->power_state == PowerState::kOn) {
target_power_state = PowerState::kLow;
metrics_info.reason = Metrics::CellularPowerOptimizationInfo::
CellularPowerOptimizationReason::kNoServiceLongNotOnline;
metrics_info.new_power_state =
Metrics::CellularPowerOptimizationInfo::PowerState::kLow;
metrics_info.since_last_online_hours =
(base::Time::Now() - device_last_online_time_).InHours();
}
break;
case PowerEvent::kUnkown:
break;
}
if (target_power_state != PowerState::kUnknown) {
bool action_result = RequestPowerStateChange(target_power_state);
if (action_result) {
current_opt_info_->power_state = target_power_state;
manager_->metrics()->NotifyCellularPowerOptimization(metrics_info);
}
}
return current_opt_info_->power_state;
}
} // namespace shill