feat(upower): read UPower's display device when there is no session bus

pull/18/head
reionwong 1 week ago
parent 8b54a65fb2
commit 27006837f8

@ -4,6 +4,7 @@
#include "cutefish/upower/battery_status.h"
#include <algorithm>
#include <cmath>
#include "base/event_loop.h"
#include "cutefish/upower/device.h"
@ -18,6 +19,9 @@ static constexpr const char* kInterface = "com.cutefish.Services.Battery";
static constexpr const char* kUPowerService = "org.freedesktop.UPower";
static constexpr const char* kUPowerPath = "/org/freedesktop/UPower";
static constexpr const char* kDisplayDevicePath = "/org/freedesktop/UPower/devices/DisplayDevice";
// UPower's device type of a battery.
static constexpr int64_t kTypeBattery = 2;
std::shared_ptr<BatteryStatus> BatteryStatus::shared()
{
@ -38,6 +42,18 @@ BatteryStatus::BatteryStatus(Connection* sessionBus, Connection* systemBus)
: m_service(sessionBus, kService, kPath, kInterface)
, m_upower(systemBus, kUPowerService, kUPowerPath, kUPowerService)
{
if (!sessionBus && systemBus) {
m_displayDevice = std::make_unique<Device>(kDisplayDevicePath, systemBus);
m_displayDeviceSubscriptions.push_back(
m_displayDevice->propertiesChanged.connect([this](const VariantMap&) { applyDisplayDevice(); }));
m_displayDeviceSubscriptions.push_back(m_displayDevice->readyChanged.connect([this] {
if (!m_ready) {
m_ready = true;
readyChanged.emit();
}
}));
}
// The signals only say that something moved; one GetAll brings every value up to date.
for (const char* signal : {"chargeStateChanged", "chargePercentChanged", "capacityChanged",
"remainingTimeChanged", "lastChargedPercentChanged", "lastChargedSecsChanged"})
@ -51,7 +67,8 @@ BatteryStatus::BatteryStatus(Connection* sessionBus, Connection* systemBus)
fetchOnBattery();
}));
fetchState();
if (!m_displayDevice)
fetchState();
fetchOnBattery();
}
@ -59,7 +76,8 @@ BatteryStatus::~BatteryStatus() = default;
void BatteryStatus::refresh()
{
m_service.send("refresh");
if (m_service.isValid())
m_service.send("refresh");
}
void BatteryStatus::fetchState()
@ -125,6 +143,28 @@ void BatteryStatus::applyState(const VariantMap& properties)
}
}
void BatteryStatus::applyDisplayDevice()
{
const VariantMap& properties = m_displayDevice->properties();
const auto present = properties.find("IsPresent");
const auto type = properties.find("Type");
const auto percentage = properties.find("Percentage");
const bool available = present != properties.end() && present->second.value<bool>(false)
&& type != properties.end() && integerValue(type->second) == kTypeBattery;
// Rounded as the service rounds it.
const int chargePercent =
percentage == properties.end() ? 0 : int(std::lround(percentage->second.value<double>(0)));
if (m_chargePercent != chargePercent) {
m_chargePercent = chargePercent;
chargePercentChanged.emit();
}
if (m_available != available) {
m_available = available;
availableChanged.emit();
}
}
void BatteryStatus::fetchOnBattery()
{
m_onBatteryCall = m_upower.getPropertyAsync("OnBattery", [this](const Reply& reply) {

@ -13,6 +13,8 @@
namespace cutefish::upower {
class Device;
// The battery as cutefish-services reports it, and whether the system runs
// on it (UPower's OnBattery). Everything is read asynchronously and kept
// here, so the getters never block.
@ -23,6 +25,9 @@ public:
// holds it.
static std::shared_ptr<BatteryStatus> shared();
// Without a session bus, as at the login screen, the level and whether
// there is a battery come from UPower's display device; the service's
// other figures stay unset.
explicit BatteryStatus(base::dbus::Connection* sessionBus = base::dbus::Connection::session(),
base::dbus::Connection* systemBus = base::dbus::Connection::system());
~BatteryStatus();
@ -62,11 +67,14 @@ public:
private:
void fetchState();
void applyState(const base::dbus::VariantMap& properties);
void applyDisplayDevice();
void fetchOnBattery();
void setOnBattery(bool onBattery);
base::dbus::Proxy m_service;
base::dbus::Proxy m_upower;
std::unique_ptr<Device> m_displayDevice;
std::vector<base::Subscription> m_displayDeviceSubscriptions;
std::vector<base::dbus::Match> m_matches;
base::dbus::PendingCall m_stateCall;
base::dbus::PendingCall m_onBatteryCall;

@ -72,6 +72,8 @@ public:
std::tuple<std::string, uint32_t, uint32_t> historyRequest;
std::unique_ptr<Object> upowerObject;
std::unique_ptr<Object> deviceObject;
double displayPercentage = 41.6;
std::unique_ptr<Object> displayObject;
private:
void run(std::promise<bool>& ready)
@ -133,11 +135,19 @@ private:
});
published = device.publish() && published;
displayObject = std::make_unique<Object>(connection.get(), "/org/freedesktop/UPower/devices/DisplayDevice",
kDeviceInterface);
displayObject->addProperty("IsPresent", [this] { return m_battery; });
displayObject->addProperty("Type", [this] { return uint32_t(m_battery ? 2 : 0); });
displayObject->addProperty("Percentage", [this] { return displayPercentage; });
published = displayObject->publish() && published;
ready.set_value(published);
loop.run();
batteryObject.reset();
upowerObject.reset();
deviceObject.reset();
displayObject.reset();
}
bool m_battery;
@ -247,6 +257,38 @@ TEST_F(UPowerTest, ThePowerSourceFollowsUPower)
ASSERT_TRUE(waitFor([&] { return m_services->inService([&] { return m_services->refreshes; }) == 1; }));
}
TEST_F(UPowerTest, WithoutASessionBusTheDisplayDeviceIsRead)
{
startServices();
BatteryStatus status(nullptr, bus());
ASSERT_TRUE(waitFor([&] { return status.isReady(); }));
EXPECT_TRUE(status.isAvailable());
EXPECT_EQ(status.chargePercent(), 42);
EXPECT_EQ(status.statusString(), "");
int percent = 0;
Subscription subscription = status.chargePercentChanged.connect([&] { ++percent; });
m_services->inService([&] {
m_services->displayPercentage = 80.2;
m_services->displayObject->emitPropertiesChanged({"Percentage"});
});
ASSERT_TRUE(waitFor([&] { return status.chargePercent() == 80; }));
EXPECT_EQ(percent, 1);
// Nothing asks a service that is not there.
status.refresh();
runFor(50ms);
EXPECT_EQ(m_services->inService([&] { return m_services->refreshes; }), 0);
}
TEST_F(UPowerTest, WithoutASessionBusOrABatteryNothingIsAvailable)
{
startServices(false);
BatteryStatus status(nullptr, bus());
ASSERT_TRUE(waitFor([&] { return status.isReady(); }));
EXPECT_FALSE(status.isAvailable());
}
TEST_F(UPowerTest, SharedStatusIsOneWhileHeld)
{
std::shared_ptr<BatteryStatus> first = BatteryStatus::shared();

Loading…
Cancel
Save