OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/power_save_blocker_impl.h" | 5 #include "content/browser/power_save_blocker_impl.h" |
6 | 6 |
7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
8 #include <X11/extensions/dpms.h> | 8 #include <X11/extensions/dpms.h> |
9 // Xlib #defines Status, but we can't have that for some of our headers. | 9 // Xlib #defines Status, but we can't have that for some of our headers. |
10 #ifdef Status | 10 #ifdef Status |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
86 // enqueues a call back to ApplyBlock() if it is true. See the comments for | 86 // enqueues a call back to ApplyBlock() if it is true. See the comments for |
87 // enqueue_apply_ below. | 87 // enqueue_apply_ below. |
88 void InitOnUIThread(); | 88 void InitOnUIThread(); |
89 | 89 |
90 // Apply or remove the power save block, respectively. These methods should be | 90 // Apply or remove the power save block, respectively. These methods should be |
91 // called once each, on the same thread, per instance. They block waiting for | 91 // called once each, on the same thread, per instance. They block waiting for |
92 // the action to complete (with a timeout); the thread must thus allow I/O. | 92 // the action to complete (with a timeout); the thread must thus allow I/O. |
93 void ApplyBlock(DBusAPI api); | 93 void ApplyBlock(DBusAPI api); |
94 void RemoveBlock(DBusAPI api); | 94 void RemoveBlock(DBusAPI api); |
95 | 95 |
96 // Asynchronous callback functions for ApplyBlock and RemoveBlock. | |
97 // Functions do not receive ownership of |response|. | |
98 void ApplyBlockFinished(DBusAPI api, dbus::Response* response); | |
99 void RemoveBlockFinished(dbus::Response* response); | |
100 | |
96 // If DPMS (the power saving system in X11) is not enabled, then we don't want | 101 // If DPMS (the power saving system in X11) is not enabled, then we don't want |
97 // to try to disable power saving, since on some desktop environments that may | 102 // to try to disable power saving, since on some desktop environments that may |
98 // enable DPMS with very poor default settings (e.g. turning off the display | 103 // enable DPMS with very poor default settings (e.g. turning off the display |
99 // after only 1 second). Must be called on the UI thread. | 104 // after only 1 second). Must be called on the UI thread. |
100 static bool DPMSEnabled(); | 105 static bool DPMSEnabled(); |
101 | 106 |
102 // Returns an appropriate D-Bus API to use based on the desktop environment. | 107 // Returns an appropriate D-Bus API to use based on the desktop environment. |
103 // Must be called on the UI thread, as it may call DPMSEnabled() above. | 108 // Must be called on the UI thread, as it may call DPMSEnabled() above. |
104 static DBusAPI SelectAPI(); | 109 static DBusAPI SelectAPI(); |
105 | 110 |
106 const PowerSaveBlockerType type_; | 111 const PowerSaveBlockerType type_; |
107 const std::string description_; | 112 const std::string description_; |
108 | 113 |
109 // Initially, we post a message to the UI thread to select an API. When it | 114 // Initially, we post a message to the UI thread to select an API. When it |
110 // finishes, it will post a message to the FILE thread to perform the actual | 115 // finishes, it will post a message to the FILE thread to perform the actual |
111 // application of the block, unless enqueue_apply_ is false. We set it to | 116 // application of the block, unless enqueue_apply_ is false. We set it to |
112 // false when we post that message, or when RemoveBlock() is called before | 117 // false when we post that message, or when RemoveBlock() is called before |
113 // ApplyBlock() has run. Both api_ and enqueue_apply_ are guarded by lock_. | 118 // ApplyBlock() has run. Both api_ and enqueue_apply_ are guarded by lock_. |
114 DBusAPI api_; | 119 DBusAPI api_; |
115 bool enqueue_apply_; | 120 bool enqueue_apply_; |
116 base::Lock lock_; | 121 base::Lock lock_; |
117 | 122 |
123 // Indicates that a D-Bus power save blocking request is in flight. | |
124 bool block_inflight_; | |
125 // Used to detect erronous redundant calls to RemoveBlock(). | |
126 bool unblock_inflight_; | |
127 // Indicates that RemoveBlock() is called before ApplyBlock() has finished. | |
128 // If it's true, then the RemoveBlock() call will be processed immediately | |
129 // after ApplyBlock() has finished. | |
130 bool unblock_enqueued_; | |
131 | |
118 scoped_refptr<dbus::Bus> bus_; | 132 scoped_refptr<dbus::Bus> bus_; |
119 | 133 |
120 // The cookie that identifies our inhibit request, | 134 // The cookie that identifies our inhibit request, |
121 // or 0 if there is no active inhibit request. | 135 // or 0 if there is no active inhibit request. |
122 uint32 inhibit_cookie_; | 136 uint32 inhibit_cookie_; |
123 | 137 |
124 DISALLOW_COPY_AND_ASSIGN(Delegate); | 138 DISALLOW_COPY_AND_ASSIGN(Delegate); |
125 }; | 139 }; |
126 | 140 |
127 PowerSaveBlockerImpl::Delegate::Delegate(PowerSaveBlockerType type, | 141 PowerSaveBlockerImpl::Delegate::Delegate(PowerSaveBlockerType type, |
128 const std::string& description) | 142 const std::string& description) |
129 : type_(type), | 143 : type_(type), |
130 description_(description), | 144 description_(description), |
131 api_(NO_API), | 145 api_(NO_API), |
132 enqueue_apply_(false), | 146 enqueue_apply_(false), |
133 inhibit_cookie_(0) { | 147 inhibit_cookie_(0) { |
134 // We're on the client's thread here, so we don't allocate the dbus::Bus | 148 // We're on the client's thread here, so we don't allocate the dbus::Bus |
135 // object yet. We'll do it later in ApplyBlock(), on the FILE thread. | 149 // object yet. We'll do it later in ApplyBlock(), on the FILE thread. |
136 } | 150 } |
137 | 151 |
138 void PowerSaveBlockerImpl::Delegate::Init() { | 152 void PowerSaveBlockerImpl::Delegate::Init() { |
139 base::AutoLock lock(lock_); | 153 base::AutoLock lock(lock_); |
140 DCHECK(!enqueue_apply_); | 154 DCHECK(!enqueue_apply_); |
141 enqueue_apply_ = true; | 155 enqueue_apply_ = true; |
156 block_inflight_ = false; | |
157 unblock_inflight_ = false; | |
158 unblock_enqueued_ = false; | |
142 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 159 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
143 base::Bind(&Delegate::InitOnUIThread, this)); | 160 base::Bind(&Delegate::InitOnUIThread, this)); |
144 } | 161 } |
145 | 162 |
146 void PowerSaveBlockerImpl::Delegate::CleanUp() { | 163 void PowerSaveBlockerImpl::Delegate::CleanUp() { |
147 base::AutoLock lock(lock_); | 164 base::AutoLock lock(lock_); |
148 if (enqueue_apply_) { | 165 if (enqueue_apply_) { |
149 // If a call to ApplyBlock() has not yet been enqueued because we are still | 166 // If a call to ApplyBlock() has not yet been enqueued because we are still |
150 // initializing on the UI thread, then just cancel it. We don't need to | 167 // initializing on the UI thread, then just cancel it. We don't need to |
151 // remove the block because we haven't even applied it yet. | 168 // remove the block because we haven't even applied it yet. |
(...skipping 13 matching lines...) Expand all Loading... | |
165 // library, so we need to use the same thread above for RemoveBlock(). It | 182 // library, so we need to use the same thread above for RemoveBlock(). It |
166 // must be a thread that allows I/O operations, so we use the FILE thread. | 183 // must be a thread that allows I/O operations, so we use the FILE thread. |
167 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 184 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, |
168 base::Bind(&Delegate::ApplyBlock, this, api_)); | 185 base::Bind(&Delegate::ApplyBlock, this, api_)); |
169 } | 186 } |
170 enqueue_apply_ = false; | 187 enqueue_apply_ = false; |
171 } | 188 } |
172 | 189 |
173 void PowerSaveBlockerImpl::Delegate::ApplyBlock(DBusAPI api) { | 190 void PowerSaveBlockerImpl::Delegate::ApplyBlock(DBusAPI api) { |
174 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 191 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
175 DCHECK(!bus_.get()); // ApplyBlock() should only be called once. | 192 DCHECK(!bus_); // ApplyBlock() should only be called once. |
193 DCHECK(!block_inflight_); | |
176 | 194 |
177 dbus::Bus::Options options; | 195 dbus::Bus::Options options; |
178 options.bus_type = dbus::Bus::SESSION; | 196 options.bus_type = dbus::Bus::SESSION; |
179 options.connection_type = dbus::Bus::PRIVATE; | 197 options.connection_type = dbus::Bus::PRIVATE; |
180 bus_ = new dbus::Bus(options); | 198 bus_ = new dbus::Bus(options); |
181 | 199 |
182 scoped_refptr<dbus::ObjectProxy> object_proxy; | 200 scoped_refptr<dbus::ObjectProxy> object_proxy; |
183 scoped_ptr<dbus::MethodCall> method_call; | 201 scoped_ptr<dbus::MethodCall> method_call; |
184 scoped_ptr<dbus::MessageWriter> message_writer; | 202 scoped_ptr<dbus::MessageWriter> message_writer; |
185 | 203 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
226 message_writer.reset(new dbus::MessageWriter(method_call.get())); | 244 message_writer.reset(new dbus::MessageWriter(method_call.get())); |
227 // The arguments of the method are: | 245 // The arguments of the method are: |
228 // app_id: The application identifier | 246 // app_id: The application identifier |
229 // reason: The reason for the inhibit | 247 // reason: The reason for the inhibit |
230 message_writer->AppendString( | 248 message_writer->AppendString( |
231 base::CommandLine::ForCurrentProcess()->GetProgram().value()); | 249 base::CommandLine::ForCurrentProcess()->GetProgram().value()); |
232 message_writer->AppendString(description_); | 250 message_writer->AppendString(description_); |
233 break; | 251 break; |
234 } | 252 } |
235 | 253 |
236 // We could do this method call asynchronously, but if we did, we'd need to | 254 block_inflight_ = true; |
237 // handle the case where we want to cancel the block before we get a reply. | 255 object_proxy->CallMethod( |
238 // We're on the FILE thread so it should be OK to block briefly here. | 256 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
239 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( | 257 base::Bind(&PowerSaveBlockerImpl::Delegate::ApplyBlockFinished, this, |
240 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 258 api)); |
259 } | |
260 | |
261 void PowerSaveBlockerImpl::Delegate::ApplyBlockFinished( | |
262 DBusAPI api, | |
263 dbus::Response* response) { | |
264 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
265 DCHECK(bus_); | |
266 DCHECK(block_inflight_); | |
267 block_inflight_ = false; | |
268 | |
241 if (response) { | 269 if (response) { |
242 // The method returns an inhibit_cookie, used to uniquely identify | 270 // The method returns an inhibit_cookie, used to uniquely identify |
243 // this request. It should be used as an argument to Uninhibit() | 271 // this request. It should be used as an argument to Uninhibit() |
244 // in order to remove the request. | 272 // in order to remove the request. |
245 dbus::MessageReader message_reader(response.get()); | 273 dbus::MessageReader message_reader(response); |
246 if (!message_reader.PopUint32(&inhibit_cookie_)) | 274 if (!message_reader.PopUint32(&inhibit_cookie_)) |
247 LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString(); | 275 LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString(); |
248 } else { | 276 } else { |
249 LOG(ERROR) << "No response to Inhibit() request!"; | 277 LOG(ERROR) << "No response to Inhibit() request!"; |
250 } | 278 } |
279 | |
280 if (unblock_enqueued_) { | |
281 unblock_enqueued_ = false; | |
282 // RemoveBlock() was called while the Inhibit operation was in flight, | |
283 // so go ahead and remove the block now. | |
284 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
285 base::Bind(&Delegate::RemoveBlock, this, api_)); | |
286 } | |
251 } | 287 } |
252 | 288 |
253 void PowerSaveBlockerImpl::Delegate::RemoveBlock(DBusAPI api) { | 289 void PowerSaveBlockerImpl::Delegate::RemoveBlock(DBusAPI api) { |
254 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 290 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
255 DCHECK(bus_.get()); // RemoveBlock() should only be called once. | 291 DCHECK(bus_); // RemoveBlock() should only be called once. |
292 DCHECK(!unblock_inflight_); | |
293 | |
294 if (block_inflight_) { | |
295 // Can't call RemoveBlock until ApplyBlock's asynchronous operation has | |
296 // finished. Enqueue it for execution once ApplyBlock is done. | |
297 unblock_enqueued_ = true; | |
miu
2015/05/15 18:43:31
Should you DCHECK(!unblock_enqueued) before this l
Kevin M
2015/05/15 18:55:25
Good idea, done.
| |
298 return; | |
299 } | |
256 | 300 |
257 scoped_refptr<dbus::ObjectProxy> object_proxy; | 301 scoped_refptr<dbus::ObjectProxy> object_proxy; |
258 scoped_ptr<dbus::MethodCall> method_call; | 302 scoped_ptr<dbus::MethodCall> method_call; |
259 | 303 |
260 switch (api) { | 304 switch (api) { |
261 case NO_API: | 305 case NO_API: |
262 NOTREACHED(); // We should never call this method with this value. | 306 NOTREACHED(); // We should never call this method with this value. |
263 return; | 307 return; |
264 case GNOME_API: | 308 case GNOME_API: |
265 object_proxy = bus_->GetObjectProxy( | 309 object_proxy = bus_->GetObjectProxy( |
266 kGnomeAPIServiceName, | 310 kGnomeAPIServiceName, |
267 dbus::ObjectPath(kGnomeAPIObjectPath)); | 311 dbus::ObjectPath(kGnomeAPIObjectPath)); |
268 method_call.reset( | 312 method_call.reset( |
269 new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit")); | 313 new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit")); |
270 break; | 314 break; |
271 case FREEDESKTOP_API: | 315 case FREEDESKTOP_API: |
272 object_proxy = bus_->GetObjectProxy( | 316 object_proxy = bus_->GetObjectProxy( |
273 kFreeDesktopAPIServiceName, | 317 kFreeDesktopAPIServiceName, |
274 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); | 318 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); |
275 method_call.reset( | 319 method_call.reset( |
276 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "UnInhibit")); | 320 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "UnInhibit")); |
277 break; | 321 break; |
278 } | 322 } |
279 | 323 |
280 dbus::MessageWriter message_writer(method_call.get()); | 324 dbus::MessageWriter message_writer(method_call.get()); |
281 message_writer.AppendUint32(inhibit_cookie_); | 325 message_writer.AppendUint32(inhibit_cookie_); |
282 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( | 326 unblock_inflight_ = true; |
283 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 327 object_proxy->CallMethod( |
328 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
329 base::Bind(&PowerSaveBlockerImpl::Delegate::RemoveBlockFinished, this)); | |
330 } | |
331 | |
332 void PowerSaveBlockerImpl::Delegate::RemoveBlockFinished( | |
333 dbus::Response* response) { | |
334 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
335 DCHECK(bus_); | |
336 unblock_inflight_ = false; | |
337 | |
284 if (!response) | 338 if (!response) |
285 LOG(ERROR) << "No response to Uninhibit() request!"; | 339 LOG(ERROR) << "No response to Uninhibit() request!"; |
286 // We don't care about checking the result. We assume it works; we can't | 340 // We don't care about checking the result. We assume it works; we can't |
287 // really do anything about it anyway if it fails. | 341 // really do anything about it anyway if it fails. |
288 inhibit_cookie_ = 0; | |
miu
2015/05/15 18:43:31
nit: Either leave this in, or adjust the header fi
Kevin M
2015/05/15 18:55:25
Done.
| |
289 | 342 |
290 bus_->ShutdownAndBlock(); | 343 bus_->ShutdownAndBlock(); |
291 bus_ = NULL; | 344 bus_ = NULL; |
292 } | 345 } |
293 | 346 |
294 // static | 347 // static |
295 bool PowerSaveBlockerImpl::Delegate::DPMSEnabled() { | 348 bool PowerSaveBlockerImpl::Delegate::DPMSEnabled() { |
296 XDisplay* display = gfx::GetXDisplay(); | 349 XDisplay* display = gfx::GetXDisplay(); |
297 BOOL enabled = false; | 350 BOOL enabled = false; |
298 int dummy; | 351 int dummy; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
330 const std::string& description) | 383 const std::string& description) |
331 : delegate_(new Delegate(type, description)) { | 384 : delegate_(new Delegate(type, description)) { |
332 delegate_->Init(); | 385 delegate_->Init(); |
333 } | 386 } |
334 | 387 |
335 PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { | 388 PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { |
336 delegate_->CleanUp(); | 389 delegate_->CleanUp(); |
337 } | 390 } |
338 | 391 |
339 } // namespace content | 392 } // namespace content |
OLD | NEW |