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 enqueue_unblock_; |
| 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 enqueue_unblock_ = 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 (enqueue_unblock_) { |
| 281 enqueue_unblock_ = 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 DCHECK(!enqueue_unblock_); |
| 296 // Can't call RemoveBlock until ApplyBlock's async operation has |
| 297 // finished. Enqueue it for execution once ApplyBlock is done. |
| 298 enqueue_unblock_ = true; |
| 299 return; |
| 300 } |
256 | 301 |
257 scoped_refptr<dbus::ObjectProxy> object_proxy; | 302 scoped_refptr<dbus::ObjectProxy> object_proxy; |
258 scoped_ptr<dbus::MethodCall> method_call; | 303 scoped_ptr<dbus::MethodCall> method_call; |
259 | 304 |
260 switch (api) { | 305 switch (api) { |
261 case NO_API: | 306 case NO_API: |
262 NOTREACHED(); // We should never call this method with this value. | 307 NOTREACHED(); // We should never call this method with this value. |
263 return; | 308 return; |
264 case GNOME_API: | 309 case GNOME_API: |
265 object_proxy = bus_->GetObjectProxy( | 310 object_proxy = bus_->GetObjectProxy( |
266 kGnomeAPIServiceName, | 311 kGnomeAPIServiceName, |
267 dbus::ObjectPath(kGnomeAPIObjectPath)); | 312 dbus::ObjectPath(kGnomeAPIObjectPath)); |
268 method_call.reset( | 313 method_call.reset( |
269 new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit")); | 314 new dbus::MethodCall(kGnomeAPIInterfaceName, "Uninhibit")); |
270 break; | 315 break; |
271 case FREEDESKTOP_API: | 316 case FREEDESKTOP_API: |
272 object_proxy = bus_->GetObjectProxy( | 317 object_proxy = bus_->GetObjectProxy( |
273 kFreeDesktopAPIServiceName, | 318 kFreeDesktopAPIServiceName, |
274 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); | 319 dbus::ObjectPath(kFreeDesktopAPIObjectPath)); |
275 method_call.reset( | 320 method_call.reset( |
276 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "UnInhibit")); | 321 new dbus::MethodCall(kFreeDesktopAPIInterfaceName, "UnInhibit")); |
277 break; | 322 break; |
278 } | 323 } |
279 | 324 |
280 dbus::MessageWriter message_writer(method_call.get()); | 325 dbus::MessageWriter message_writer(method_call.get()); |
281 message_writer.AppendUint32(inhibit_cookie_); | 326 message_writer.AppendUint32(inhibit_cookie_); |
282 scoped_ptr<dbus::Response> response(object_proxy->CallMethodAndBlock( | 327 unblock_inflight_ = true; |
283 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | 328 object_proxy->CallMethod( |
| 329 method_call.get(), dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| 330 base::Bind(&PowerSaveBlockerImpl::Delegate::RemoveBlockFinished, this)); |
| 331 } |
| 332 |
| 333 void PowerSaveBlockerImpl::Delegate::RemoveBlockFinished( |
| 334 dbus::Response* response) { |
| 335 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 336 DCHECK(bus_); |
| 337 unblock_inflight_ = false; |
| 338 |
284 if (!response) | 339 if (!response) |
285 LOG(ERROR) << "No response to Uninhibit() request!"; | 340 LOG(ERROR) << "No response to Uninhibit() request!"; |
286 // We don't care about checking the result. We assume it works; we can't | 341 // 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. | 342 // really do anything about it anyway if it fails. |
288 inhibit_cookie_ = 0; | 343 inhibit_cookie_ = 0; |
289 | 344 |
290 bus_->ShutdownAndBlock(); | 345 bus_->ShutdownAndBlock(); |
291 bus_ = NULL; | 346 bus_ = NULL; |
292 } | 347 } |
293 | 348 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 const std::string& description) | 385 const std::string& description) |
331 : delegate_(new Delegate(type, description)) { | 386 : delegate_(new Delegate(type, description)) { |
332 delegate_->Init(); | 387 delegate_->Init(); |
333 } | 388 } |
334 | 389 |
335 PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { | 390 PowerSaveBlockerImpl::~PowerSaveBlockerImpl() { |
336 delegate_->CleanUp(); | 391 delegate_->CleanUp(); |
337 } | 392 } |
338 | 393 |
339 } // namespace content | 394 } // namespace content |
OLD | NEW |