Chromium Code Reviews| 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 |