[M120][Tizen][Onscreen] Fix build errors for TV profile
[platform/framework/web/chromium-efl.git] / chrome / browser / dbus_memory_pressure_evaluator_linux.cc
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/dbus_memory_pressure_evaluator_linux.h"
6
7 #include <utility>
8
9 #include "base/containers/contains.h"
10 #include "base/functional/bind.h"
11 #include "base/logging.h"
12 #include "base/memory/memory_pressure_listener.h"
13 #include "base/memory/scoped_refptr.h"
14 #include "chrome/common/chrome_features.h"
15 #include "components/dbus/thread_linux/dbus_thread_linux.h"
16 #include "dbus/message.h"
17 #include "dbus/object_path.h"
18 #include "dbus/object_proxy.h"
19
20 namespace {
21
22 scoped_refptr<dbus::Bus> CreateBusOfType(dbus::Bus::BusType type) {
23   dbus::Bus::Options options;
24   options.bus_type = type;
25   options.connection_type = dbus::Bus::PRIVATE;
26   options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
27   return base::MakeRefCounted<dbus::Bus>(options);
28 }
29
30 }  // namespace
31
32 const char DbusMemoryPressureEvaluatorLinux::kMethodNameHasOwner[] =
33     "NameHasOwner";
34 const char DbusMemoryPressureEvaluatorLinux::kMethodListActivatableNames[] =
35     "ListActivatableNames";
36
37 const char DbusMemoryPressureEvaluatorLinux::kLmmService[] =
38     "org.freedesktop.LowMemoryMonitor";
39 const char DbusMemoryPressureEvaluatorLinux::kLmmObject[] =
40     "/org/freedesktop/LowMemoryMonitor";
41 const char DbusMemoryPressureEvaluatorLinux::kLmmInterface[] =
42     "org.freedesktop.LowMemoryMonitor";
43
44 const char DbusMemoryPressureEvaluatorLinux::kXdgPortalService[] =
45     "org.freedesktop.portal.Desktop";
46 const char DbusMemoryPressureEvaluatorLinux::kXdgPortalObject[] =
47     "/org/freedesktop/portal/desktop";
48 const char
49     DbusMemoryPressureEvaluatorLinux::kXdgPortalMemoryMonitorInterface[] =
50         "org.freedesktop.portal.MemoryMonitor";
51
52 const char DbusMemoryPressureEvaluatorLinux::kLowMemoryWarningSignal[] =
53     "LowMemoryWarning";
54
55 // LMM emits signals every 15 seconds on pressure, so if we've been quiet for 20
56 // seconds, the pressure is likely cleared up.
57 const base::TimeDelta DbusMemoryPressureEvaluatorLinux::kResetVotePeriod =
58     base::Seconds(20);
59
60 DbusMemoryPressureEvaluatorLinux::DbusMemoryPressureEvaluatorLinux(
61     std::unique_ptr<memory_pressure::MemoryPressureVoter> voter)
62     : DbusMemoryPressureEvaluatorLinux(std::move(voter), nullptr, nullptr) {
63   // Only start the service checks in the public constructor, so the tests can
64   // have time to set up mocks first when using the private constructor.
65   CheckIfLmmIsAvailable();
66 }
67
68 DbusMemoryPressureEvaluatorLinux::DbusMemoryPressureEvaluatorLinux(
69     std::unique_ptr<memory_pressure::MemoryPressureVoter> voter,
70     scoped_refptr<dbus::Bus> system_bus,
71     scoped_refptr<dbus::Bus> session_bus)
72     : memory_pressure::SystemMemoryPressureEvaluator(std::move(voter)),
73       system_bus_(system_bus),
74       session_bus_(session_bus) {
75   moderate_level_ = features::kLinuxLowMemoryMonitorModerateLevel.Get();
76   critical_level_ = features::kLinuxLowMemoryMonitorCriticalLevel.Get();
77
78   CHECK(critical_level_ > moderate_level_);
79 }
80
81 DbusMemoryPressureEvaluatorLinux::~DbusMemoryPressureEvaluatorLinux() {
82   if (system_bus_) {
83     system_bus_->ShutdownOnDBusThreadAndBlock();
84     system_bus_.reset();
85   }
86
87   if (session_bus_) {
88     session_bus_->ShutdownOnDBusThreadAndBlock();
89     session_bus_.reset();
90   }
91 }
92
93 void DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailable() {
94   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
95
96   if (!system_bus_)
97     system_bus_ = CreateBusOfType(dbus::Bus::SYSTEM);
98
99   CheckIfServiceIsAvailable(
100       system_bus_, kLmmService,
101       base::BindOnce(
102           &DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailableResponse,
103           weak_ptr_factory_.GetWeakPtr()));
104 }
105
106 void DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailableResponse(
107     bool is_available) {
108   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
109
110   if (is_available) {
111     VLOG(1) << "LMM is available, using " << kLmmInterface;
112
113     object_proxy_ =
114         system_bus_->GetObjectProxy(kLmmService, dbus::ObjectPath(kLmmObject));
115     object_proxy_->ConnectToSignal(
116         kLmmInterface, kLowMemoryWarningSignal,
117         base::BindRepeating(
118             &DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning,
119             weak_ptr_factory_.GetWeakPtr()),
120         base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnSignalConnected,
121                        weak_ptr_factory_.GetWeakPtr()));
122   } else {
123     VLOG(1) << "LMM is not available, checking for portal";
124
125     ResetBus(system_bus_);
126     CheckIfPortalIsAvailable();
127   }
128 }
129
130 void DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailable() {
131   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
132
133   if (!session_bus_)
134     session_bus_ = CreateBusOfType(dbus::Bus::SESSION);
135
136   CheckIfServiceIsAvailable(
137       session_bus_, kXdgPortalService,
138       base::BindOnce(
139           &DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailableResponse,
140           weak_ptr_factory_.GetWeakPtr()));
141 }
142
143 void DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailableResponse(
144     bool is_available) {
145   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
146
147   if (is_available) {
148     VLOG(1) << "Portal is available, using "
149             << kXdgPortalMemoryMonitorInterface;
150
151     object_proxy_ = session_bus_->GetObjectProxy(
152         kXdgPortalService, dbus::ObjectPath(kXdgPortalObject));
153     object_proxy_->ConnectToSignal(
154         kXdgPortalMemoryMonitorInterface, kLowMemoryWarningSignal,
155         base::BindRepeating(
156             &DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning,
157             weak_ptr_factory_.GetWeakPtr()),
158         base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnSignalConnected,
159                        weak_ptr_factory_.GetWeakPtr()));
160   } else {
161     VLOG(1) << "No memory monitor found";
162
163     ResetBus(session_bus_);
164   }
165 }
166
167 void DbusMemoryPressureEvaluatorLinux::CheckIfServiceIsAvailable(
168     scoped_refptr<dbus::Bus> bus,
169     const std::string& service,
170     base::OnceCallback<void(bool)> callback) {
171   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
172
173   dbus::ObjectProxy* dbus_proxy =
174       bus->GetObjectProxy(DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
175
176   dbus::MethodCall method_call(DBUS_INTERFACE_DBUS, kMethodNameHasOwner);
177   dbus::MessageWriter writer(&method_call);
178   writer.AppendString(service);
179
180   dbus_proxy->CallMethod(
181       &method_call, DBUS_TIMEOUT_USE_DEFAULT,
182       base::BindOnce(&DbusMemoryPressureEvaluatorLinux::OnNameHasOwnerResponse,
183                      weak_ptr_factory_.GetWeakPtr(), std::move(bus), service,
184                      std::move(callback)));
185 }
186
187 void DbusMemoryPressureEvaluatorLinux::OnNameHasOwnerResponse(
188     scoped_refptr<dbus::Bus> bus,
189     const std::string& service,
190     base::OnceCallback<void(bool)> callback,
191     dbus::Response* response) {
192   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
193
194   bool is_running = false;
195
196   if (response) {
197     dbus::MessageReader reader(response);
198     bool owned = false;
199
200     if (!reader.PopBool(&owned)) {
201       LOG(ERROR) << "Failed to read " << kMethodNameHasOwner << " response";
202     } else if (owned) {
203       is_running = true;
204     }
205   } else {
206     LOG(ERROR) << "Failed to call " << kMethodNameHasOwner;
207   }
208
209   if (is_running) {
210     std::move(callback).Run(true);
211   } else {
212     dbus::ObjectProxy* dbus_proxy = bus->GetObjectProxy(
213         DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS));
214
215     dbus::MethodCall method_call(DBUS_INTERFACE_DBUS,
216                                  kMethodListActivatableNames);
217     dbus_proxy->CallMethod(
218         &method_call, DBUS_TIMEOUT_USE_DEFAULT,
219         base::BindOnce(
220             &DbusMemoryPressureEvaluatorLinux::OnListActivatableNamesResponse,
221             weak_ptr_factory_.GetWeakPtr(), std::move(service),
222             std::move(callback)));
223   }
224 }
225
226 void DbusMemoryPressureEvaluatorLinux::OnListActivatableNamesResponse(
227     const std::string& service,
228     base::OnceCallback<void(bool)> callback,
229     dbus::Response* response) {
230   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
231
232   bool is_activatable = false;
233
234   if (response) {
235     dbus::MessageReader reader(response);
236     std::vector<std::string> names;
237     if (!reader.PopArrayOfStrings(&names)) {
238       LOG(ERROR) << "Failed to read " << kMethodListActivatableNames
239                  << " response";
240     } else if (base::Contains(names, service)) {
241       is_activatable = true;
242     }
243   } else {
244     LOG(ERROR) << "Failed to call " << kMethodListActivatableNames;
245   }
246
247   std::move(callback).Run(is_activatable);
248 }
249
250 void DbusMemoryPressureEvaluatorLinux::ResetBus(scoped_refptr<dbus::Bus>& bus) {
251   if (!bus)
252     return;
253
254   bus->GetDBusTaskRunner()->PostTask(
255       FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus));
256   bus.reset();
257 }
258
259 void DbusMemoryPressureEvaluatorLinux::OnSignalConnected(
260     const std::string& interface,
261     const std::string& signal,
262     bool connected) {
263   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
264
265   if (!connected) {
266     LOG(WARNING) << "Failed to connect to " << interface << '.' << signal;
267
268     ResetBus(system_bus_);
269     ResetBus(session_bus_);
270   }
271 }
272
273 void DbusMemoryPressureEvaluatorLinux::OnLowMemoryWarning(
274     dbus::Signal* signal) {
275   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
276
277   dbus::MessageReader reader(signal);
278   uint8_t lmm_level;
279   if (!reader.PopByte(&lmm_level)) {
280     LOG(WARNING) << "Failed to parse low memory level";
281     return;
282   }
283
284   // static_cast is needed as lmm_level is a uint8_t, which is often an alias to
285   // char, meaning that sending it to the output stream would just print the
286   // character representation rather than the numeric representation.
287   VLOG(1) << "Monitor sent memory pressure level: "
288           << static_cast<int>(lmm_level);
289
290   base::MemoryPressureListener::MemoryPressureLevel new_level =
291       LmmToBasePressureLevel(lmm_level);
292
293   VLOG(1) << "MemoryPressureLevel: " << new_level;
294   UpdateLevel(new_level);
295 }
296
297 base::MemoryPressureListener::MemoryPressureLevel
298 DbusMemoryPressureEvaluatorLinux::LmmToBasePressureLevel(uint8_t lmm_level) {
299   if (lmm_level >= critical_level_)
300     return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
301   if (lmm_level >= moderate_level_)
302     return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
303   return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
304 }
305
306 void DbusMemoryPressureEvaluatorLinux::UpdateLevel(
307     base::MemoryPressureListener::MemoryPressureLevel new_level) {
308   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
309
310   reset_vote_timer_.Stop();
311
312   SetCurrentVote(new_level);
313   switch (new_level) {
314     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
315       // By convention no notifications are sent when returning to NONE level.
316       SendCurrentVote(false);
317       break;
318     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
319     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
320       SendCurrentVote(true);
321
322       reset_vote_timer_.Start(
323           FROM_HERE, kResetVotePeriod,
324           base::BindOnce(
325               &DbusMemoryPressureEvaluatorLinux::UpdateLevel,
326               weak_ptr_factory_.GetWeakPtr(),
327               base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE));
328       break;
329   }
330 }