blob: 362dde977f8e549a91cf58267b81c163c3179c37 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager/powerd/system/peripheral_battery_watcher.h"
#include <fcntl.h>
#include <cerrno>
#include <string>
#include <base/bind.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include "power_manager/common/util.h"
#include "power_manager/powerd/system/dbus_wrapper.h"
#include "power_manager/proto_bindings/peripheral_battery_status.pb.h"
namespace power_manager {
namespace system {
namespace {
// Default path examined for peripheral battery directories.
const char kDefaultPeripheralBatteryPath[] = "/sys/class/power_supply/";
// Default interval for polling the device battery info.
const int kDefaultPollIntervalMs = 600000;
// Reads |path| to |value_out| and trims trailing whitespace. False is returned
// if the file doesn't exist or can't be read.
bool ReadStringFromFile(const base::FilePath& path, std::string* value_out) {
if (!base::ReadFileToString(path, value_out))
return false;
base::TrimWhitespaceASCII(*value_out, base::TRIM_TRAILING, value_out);
return true;
}
} // namespace
const char PeripheralBatteryWatcher::kScopeFile[] = "scope";
const char PeripheralBatteryWatcher::kScopeValueDevice[] = "Device";
const char PeripheralBatteryWatcher::kStatusFile[] = "status";
const char PeripheralBatteryWatcher::kStatusValueUnknown[] = "Unknown";
const char PeripheralBatteryWatcher::kModelNameFile[] = "model_name";
const char PeripheralBatteryWatcher::kCapacityFile[] = "capacity";
PeripheralBatteryWatcher::PeripheralBatteryWatcher()
: dbus_wrapper_(nullptr),
peripheral_battery_path_(kDefaultPeripheralBatteryPath),
poll_interval_ms_(kDefaultPollIntervalMs) {}
PeripheralBatteryWatcher::~PeripheralBatteryWatcher() {}
void PeripheralBatteryWatcher::Init(DBusWrapperInterface* dbus_wrapper) {
dbus_wrapper_ = dbus_wrapper;
ReadBatteryStatuses();
}
void PeripheralBatteryWatcher::GetBatteryList(
std::vector<base::FilePath>* battery_list) {
battery_list->clear();
base::FileEnumerator dir_enumerator(peripheral_battery_path_, false,
base::FileEnumerator::DIRECTORIES);
for (base::FilePath device_path = dir_enumerator.Next(); !device_path.empty();
device_path = dir_enumerator.Next()) {
// Peripheral batteries have device scopes.
std::string scope;
if (!ReadStringFromFile(device_path.Append(kScopeFile), &scope) ||
scope != kScopeValueDevice)
continue;
// Some devices may initially have an unknown status; avoid reporting
// them: http://b/64392016
std::string status;
if (ReadStringFromFile(device_path.Append(kStatusFile), &status) &&
status == kStatusValueUnknown)
continue;
battery_list->push_back(device_path);
}
}
void PeripheralBatteryWatcher::ReadBatteryStatuses() {
battery_readers_.clear();
std::vector<base::FilePath> new_battery_list;
GetBatteryList(&new_battery_list);
for (size_t i = 0; i < new_battery_list.size(); i++) {
base::FilePath path = new_battery_list[i];
// sysfs entry "capacity" has the current battery level.
base::FilePath capacity_path = path.Append(kCapacityFile);
if (!base::PathExists(capacity_path))
continue;
std::string model_name;
if (!ReadStringFromFile(path.Append(kModelNameFile), &model_name))
continue;
battery_readers_.push_back(std::make_unique<AsyncFileReader>());
AsyncFileReader* reader = battery_readers_.back().get();
if (reader->Init(capacity_path)) {
reader->StartRead(base::Bind(&PeripheralBatteryWatcher::ReadCallback,
base::Unretained(this), path, model_name),
base::Bind(&PeripheralBatteryWatcher::ErrorCallback,
base::Unretained(this), path, model_name));
} else {
LOG(ERROR) << "Can't read battery capacity " << capacity_path.value();
}
}
poll_timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(poll_interval_ms_), this,
&PeripheralBatteryWatcher::ReadBatteryStatuses);
}
void PeripheralBatteryWatcher::SendBatteryStatus(const base::FilePath& path,
const std::string& model_name,
int level) {
PeripheralBatteryStatus proto;
proto.set_path(path.value());
proto.set_name(model_name);
if (level >= 0)
proto.set_level(level);
dbus_wrapper_->EmitSignalWithProtocolBuffer(kPeripheralBatteryStatusSignal,
proto);
}
void PeripheralBatteryWatcher::ReadCallback(const base::FilePath& path,
const std::string& model_name,
const std::string& data) {
std::string trimmed_data;
base::TrimWhitespaceASCII(data, base::TRIM_ALL, &trimmed_data);
int level = -1;
if (base::StringToInt(trimmed_data, &level)) {
SendBatteryStatus(path, model_name, level);
} else {
LOG(ERROR) << "Invalid battery level reading : [" << data << "]"
<< " from " << path.value();
}
}
void PeripheralBatteryWatcher::ErrorCallback(const base::FilePath& path,
const std::string& model_name) {
SendBatteryStatus(path, model_name, -1);
}
} // namespace system
} // namespace power_manager