123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- // Copyright 2021 The Chromium 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 "chrome/browser/dbus_memory_pressure_evaluator_linux.h"
- #include <utility>
- #include "base/bind.h"
- #include "base/containers/contains.h"
- #include "base/logging.h"
- #include "base/memory/memory_pressure_listener.h"
- #include "base/memory/scoped_refptr.h"
- #include "chrome/common/chrome_features.h"
- #include "components/dbus/thread_linux/dbus_thread_linux.h"
- #include "dbus/message.h"
- #include "dbus/object_path.h"
- #include "dbus/object_proxy.h"
- namespace {
- scoped_refptr<dbus::Bus> CreateBusOfType(dbus::Bus::BusType type) {
- dbus::Bus::Options options;
- options.bus_type = type;
- options.connection_type = dbus::Bus::PRIVATE;
- options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
- return base::MakeRefCounted<dbus::Bus>(options);
- }
- } // namespace
- const char DbusMemoryPressureEvaluatorLinux::kMethodNameHasOwner[] =
- "NameHasOwner";
- const char DbusMemoryPressureEvaluatorLinux::kMethodListActivatableNames[] =
- "ListActivatableNames";
- const char DbusMemoryPressureEvaluatorLinux::kLmmService[] =
- "org.freedesktop.LowMemoryMonitor";
- const char DbusMemoryPressureEvaluatorLinux::kLmmObject[] =
- "/org/freedesktop/LowMemoryMonitor";
- const char DbusMemoryPressureEvaluatorLinux::kLmmInterface[] =
- "org.freedesktop.LowMemoryMonitor";
- const char DbusMemoryPressureEvaluatorLinux::kXdgPortalService[] =
- "org.freedesktop.portal.Desktop";
- const char DbusMemoryPressureEvaluatorLinux::kXdgPortalObject[] =
- "/org/freedesktop/portal/desktop";
- const char
- DbusMemoryPressureEvaluatorLinux::kXdgPortalMemoryMonitorInterface[] =
- "org.freedesktop.portal.MemoryMonitor";
- const char DbusMemoryPressureEvaluatorLinux::kLowMemoryWarningSignal[] =
- "LowMemoryWarning";
- // LMM emits signals every 15 seconds on pressure, so if we've been quiet for 20
- // seconds, the pressure is likely cleared up.
- const base::TimeDelta DbusMemoryPressureEvaluatorLinux::kResetVotePeriod =
- base::Seconds(20);
- DbusMemoryPressureEvaluatorLinux::DbusMemoryPressureEvaluatorLinux(
- std::unique_ptr<memory_pressure::MemoryPressureVoter> voter)
- : DbusMemoryPressureEvaluatorLinux(std::move(voter), nullptr, nullptr) {
- // Only start the service checks in the public constructor, so the tests can
- // have time to set up mocks first when using the private constructor.
- CheckIfLmmIsAvailable();
- }
- DbusMemoryPressureEvaluatorLinux::DbusMemoryPressureEvaluatorLinux(
- std::unique_ptr<memory_pressure::MemoryPressureVoter> voter,
- scoped_refptr<dbus::Bus> system_bus,
- scoped_refptr<dbus::Bus> session_bus)
- : memory_pressure::SystemMemoryPressureEvaluator(std::move(voter)),
- system_bus_(system_bus),
- session_bus_(session_bus) {
- moderate_level_ = features::kLinuxLowMemoryMonitorModerateLevel.Get();
- critical_level_ = features::kLinuxLowMemoryMonitorCriticalLevel.Get();
- CHECK(critical_level_ > moderate_level_);
- }
- DbusMemoryPressureEvaluatorLinux::~DbusMemoryPressureEvaluatorLinux() {
- if (system_bus_) {
- system_bus_->ShutdownOnDBusThreadAndBlock();
- system_bus_.reset();
- }
- if (session_bus_) {
- session_bus_->ShutdownOnDBusThreadAndBlock();
- session_bus_.reset();
- }
- }
- void DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailable() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!system_bus_)
- system_bus_ = CreateBusOfType(dbus::Bus::SYSTEM);
- CheckIfServiceIsAvailable(
- system_bus_, kLmmService,
- base::BindOnce(
- &DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailableResponse,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailableResponse(
- bool is_available) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (is_available) {
- VLOG(1) << "LMM is available, using " << kLmmInterface;
- object_proxy_ =
- system_bus_->GetObjectProxy(kLmmService, dbus::ObjectPath(kLmmObject));
- object_proxy_->ConnectToSignal(
- kLmmInterface, kLowMemoryWarningSignal,
- base::BindRepeating(
- &DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnSignalConnected,
- weak_ptr_factory_.GetWeakPtr()));
- } else {
- VLOG(1) << "LMM is not available, checking for portal";
- ResetBus(system_bus_);
- CheckIfPortalIsAvailable();
- }
- }
- void DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailable() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!session_bus_)
- session_bus_ = CreateBusOfType(dbus::Bus::SESSION);
- CheckIfServiceIsAvailable(
- session_bus_, kXdgPortalService,
- base::BindOnce(
- &DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailableResponse,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailableResponse(
- bool is_available) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (is_available) {
- VLOG(1) << "Portal is available, using "
- << kXdgPortalMemoryMonitorInterface;
- object_proxy_ = session_bus_->GetObjectProxy(
- kXdgPortalService, dbus::ObjectPath(kXdgPortalObject));
- object_proxy_->ConnectToSignal(
- kXdgPortalMemoryMonitorInterface, kLowMemoryWarningSignal,
- base::BindRepeating(
- &DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnSignalConnected,
- weak_ptr_factory_.GetWeakPtr()));
- } else {
- VLOG(1) << "No memory monitor found";
- ResetBus(session_bus_);
- }
- }
- void DbusMemoryPressureEvaluatorLinux::CheckIfServiceIsAvailable(
- scoped_refptr<dbus::Bus> bus,
- const std::string& service,
- base::OnceCallback<void(bool)> callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- dbus::ObjectProxy* dbus_proxy =
- bus->GetObjectProxy(DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
- dbus::MethodCall method_call(DBUS_INTERFACE_DBUS, kMethodNameHasOwner);
- dbus::MessageWriter writer(&method_call);
- writer.AppendString(service);
- dbus_proxy->CallMethod(
- &method_call, DBUS_TIMEOUT_USE_DEFAULT,
- base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnNameHasOwnerResponse,
- weak_ptr_factory_.GetWeakPtr(), std::move(bus), service,
- std::move(callback)));
- }
- void DbusMemoryPressureEvaluatorLinux::OnNameHasOwnerResponse(
- scoped_refptr<dbus::Bus> bus,
- const std::string& service,
- base::OnceCallback<void(bool)> callback,
- dbus::Response* response) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool is_running = false;
- if (response) {
- dbus::MessageReader reader(response);
- bool owned = false;
- if (!reader.PopBool(&owned)) {
- LOG(ERROR) << "Failed to read " << kMethodNameHasOwner << " response";
- } else if (owned) {
- is_running = true;
- }
- } else {
- LOG(ERROR) << "Failed to call " << kMethodNameHasOwner;
- }
- if (is_running) {
- std::move(callback).Run(true);
- } else {
- dbus::ObjectProxy* dbus_proxy = bus->GetObjectProxy(
- DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
- dbus::MethodCall method_call(DBUS_INTERFACE_DBUS,
- kMethodListActivatableNames);
- dbus_proxy->CallMethod(
- &method_call, DBUS_TIMEOUT_USE_DEFAULT,
- base::BindOnce(
- &DbusMemoryPressureEvaluatorLinux::OnListActivatableNamesResponse,
- weak_ptr_factory_.GetWeakPtr(), std::move(service),
- std::move(callback)));
- }
- }
- void DbusMemoryPressureEvaluatorLinux::OnListActivatableNamesResponse(
- const std::string& service,
- base::OnceCallback<void(bool)> callback,
- dbus::Response* response) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool is_activatable = false;
- if (response) {
- dbus::MessageReader reader(response);
- std::vector<std::string> names;
- if (!reader.PopArrayOfStrings(&names)) {
- LOG(ERROR) << "Failed to read " << kMethodListActivatableNames
- << " response";
- } else if (base::Contains(names, service)) {
- is_activatable = true;
- }
- } else {
- LOG(ERROR) << "Failed to call " << kMethodListActivatableNames;
- }
- std::move(callback).Run(is_activatable);
- }
- void DbusMemoryPressureEvaluatorLinux::ResetBus(scoped_refptr<dbus::Bus>& bus) {
- if (!bus)
- return;
- bus->GetDBusTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus));
- bus.reset();
- }
- void DbusMemoryPressureEvaluatorLinux::OnSignalConnected(
- const std::string& interface,
- const std::string& signal,
- bool connected) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!connected) {
- LOG(WARNING) << "Failed to connect to " << interface << '.' << signal;
- ResetBus(system_bus_);
- ResetBus(session_bus_);
- }
- }
- void DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning(
- dbus::Signal* signal) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- dbus::MessageReader reader(signal);
- uint8_t lmm_level;
- if (!reader.PopByte(&lmm_level)) {
- LOG(WARNING) << "Failed to parse low memory level";
- return;
- }
- // static_cast is needed as lmm_level is a uint8_t, which is often an alias to
- // char, meaning that sending it to the output stream would just print the
- // character representation rather than the numeric representation.
- VLOG(1) << "Monitor sent memory pressure level: "
- << static_cast<int>(lmm_level);
- base::MemoryPressureListener::MemoryPressureLevel new_level =
- LmmToBasePressureLevel(lmm_level);
- VLOG(1) << "MemoryPressureLevel: " << new_level;
- UpdateLevel(new_level);
- }
- base::MemoryPressureListener::MemoryPressureLevel
- DbusMemoryPressureEvaluatorLinux::LmmToBasePressureLevel(uint8_t lmm_level) {
- if (lmm_level >= critical_level_)
- return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
- if (lmm_level >= moderate_level_)
- return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
- return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
- }
- void DbusMemoryPressureEvaluatorLinux::UpdateLevel(
- base::MemoryPressureListener::MemoryPressureLevel new_level) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- reset_vote_timer_.Stop();
- SetCurrentVote(new_level);
- switch (new_level) {
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
- // By convention no notifications are sent when returning to NONE level.
- SendCurrentVote(false);
- break;
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
- case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
- SendCurrentVote(true);
- reset_vote_timer_.Start(
- FROM_HERE, kResetVotePeriod,
- base::BindOnce(
- &DbusMemoryPressureEvaluatorLinux::UpdateLevel,
- weak_ptr_factory_.GetWeakPtr(),
- base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE));
- break;
- }
- }
|