diff options
author | Daniel Erat <derat@google.com> | 2015-10-06 16:54:12 -0600 |
---|---|---|
committer | Daniel Erat <derat@google.com> | 2015-10-07 17:13:14 -0600 |
commit | e4d4a81d6b56d66970f0332c6db2b898f60510c8 (patch) | |
tree | 48e8e7c47f22f6f487c20eef49056c6ac18e9022 | |
parent | c2a4b05a931fbf3ecbf1915049b549b33dae8ba5 (diff) | |
download | nativepower-e4d4a81d6b56d66970f0332c6db2b898f60510c8.tar.gz |
Support suspending the system immediately.
Add PowerManagerClient::Suspend(), which instructs
nativepowerman to write "mem" to /sys/power/state.
Bug: 24672953
Change-Id: Icd5d5e8a28a784d632a8e8b7168421acd92be1dd
-rw-r--r-- | client/power_manager_client.cc | 13 | ||||
-rw-r--r-- | client/power_manager_client_unittest.cc | 14 | ||||
-rw-r--r-- | daemon/power_manager.cc | 39 | ||||
-rw-r--r-- | daemon/power_manager.h | 16 | ||||
-rw-r--r-- | daemon/power_manager_stub.cc | 26 | ||||
-rw-r--r-- | daemon/power_manager_unittest.cc | 48 | ||||
-rw-r--r-- | example/power_example.cc | 10 | ||||
-rw-r--r-- | include/nativepower/power_manager_client.h | 31 | ||||
-rw-r--r-- | include/nativepower/power_manager_stub.h | 24 |
9 files changed, 213 insertions, 8 deletions
diff --git a/client/power_manager_client.cc b/client/power_manager_client.cc index 6dd1a24..20c437a 100644 --- a/client/power_manager_client.cc +++ b/client/power_manager_client.cc @@ -93,6 +93,19 @@ std::unique_ptr<WakeLock> PowerManagerClient::CreateWakeLock( return lock; } +bool PowerManagerClient::Suspend(base::TimeDelta event_uptime, + SuspendReason reason, + int flags) { + DCHECK(power_manager_.get()); + status_t status = power_manager_->goToSleep( + event_uptime.InMilliseconds(), static_cast<int>(reason), flags); + if (status != OK) { + LOG(ERROR) << "Suspend request failed with status " << status; + return false; + } + return true; +} + bool PowerManagerClient::ShutDown(ShutdownReason reason) { DCHECK(power_manager_.get()); status_t status = power_manager_->shutdown(false /* confirm */, diff --git a/client/power_manager_client_unittest.cc b/client/power_manager_client_unittest.cc index e3a6c70..f27265e 100644 --- a/client/power_manager_client_unittest.cc +++ b/client/power_manager_client_unittest.cc @@ -16,6 +16,7 @@ #include <base/logging.h> #include <base/macros.h> +#include <base/time/time.h> #include <binderwrapper/binder_test_base.h> #include <binderwrapper/stub_binder_wrapper.h> #include <nativepower/constants.h> @@ -44,6 +45,19 @@ class PowerManagerClientTest : public BinderTestBase { DISALLOW_COPY_AND_ASSIGN(PowerManagerClientTest); }; +TEST_F(PowerManagerClientTest, Suspend) { + EXPECT_EQ(0, power_manager_->num_suspend_requests()); + + const auto kEventTime = base::TimeDelta::FromMilliseconds(123); + const int kFlags = 0x456; + EXPECT_TRUE(client_.Suspend(kEventTime, SuspendReason::POWER_BUTTON, kFlags)); + EXPECT_EQ(1, power_manager_->num_suspend_requests()); + EXPECT_EQ(PowerManagerStub::ConstructSuspendRequestString( + kEventTime.InMilliseconds(), + static_cast<int>(SuspendReason::POWER_BUTTON), kFlags), + power_manager_->GetSuspendRequestString(0)); +} + TEST_F(PowerManagerClientTest, ShutDown) { EXPECT_TRUE(client_.ShutDown(ShutdownReason::DEFAULT)); ASSERT_EQ(1u, power_manager_->shutdown_reasons().size()); diff --git a/daemon/power_manager.cc b/daemon/power_manager.cc index 1c362f1..bb642b1 100644 --- a/daemon/power_manager.cc +++ b/daemon/power_manager.cc @@ -16,7 +16,9 @@ #include "power_manager.h" +#include <base/files/file_util.h> #include <base/logging.h> +#include <base/sys_info.h> #include <binderwrapper/binder_wrapper.h> #include <cutils/android_reboot.h> #include <nativepower/constants.h> @@ -25,11 +27,19 @@ #include <utils/String8.h> namespace android { +namespace { + +// Path to real sysfs file that can be written to change the power state. +const char kDefaultPowerStatePath[] = "/sys/power/state"; + +} // namespace const char PowerManager::kRebootPrefix[] = "reboot,"; const char PowerManager::kShutdownPrefix[] = "shutdown,"; +const char PowerManager::kPowerStateSuspend[] = "mem"; -PowerManager::PowerManager() = default; +PowerManager::PowerManager() + : power_state_path_(kDefaultPowerStatePath) {} PowerManager::~PowerManager() = default; @@ -42,8 +52,8 @@ bool PowerManager::Init() { return false; } - LOG(INFO) << "Registering with service manager as " - << kPowerManagerServiceName; + LOG(INFO) << "Registering with service manager as \"" + << kPowerManagerServiceName << "\""; return BinderWrapper::Get()->RegisterService(kPowerManagerServiceName, this); } @@ -85,8 +95,27 @@ status_t PowerManager::powerHint(int hintId, int data) { } status_t PowerManager::goToSleep(int64_t event_time_ms, int reason, int flags) { - NOTIMPLEMENTED() << "goToSleep: event_time_ms=" << event_time_ms - << " reason=" << reason << " flags=" << flags; + if (event_time_ms < last_resume_uptime_.InMilliseconds()) { + LOG(WARNING) << "Ignoring request to suspend in response to event at " + << event_time_ms << " preceding last resume time " + << last_resume_uptime_.InMilliseconds(); + return BAD_VALUE; + } + + LOG(INFO) << "Suspending immediately for event at " << event_time_ms + << " (reason=" << reason << " flags=" << flags << ")"; + if (base::WriteFile(power_state_path_, kPowerStateSuspend, + strlen(kPowerStateSuspend)) != + static_cast<int>(strlen(kPowerStateSuspend))) { + PLOG(ERROR) << "Failed to write \"" << kPowerStateSuspend << "\" to " + << power_state_path_.value(); + return UNKNOWN_ERROR; + } + + last_resume_uptime_ = + base::TimeDelta::FromMilliseconds(base::SysInfo::Uptime()); + LOG(INFO) << "Resumed from suspend at " + << last_resume_uptime_.InMilliseconds(); return OK; } diff --git a/daemon/power_manager.h b/daemon/power_manager.h index f4aa832..0428b81 100644 --- a/daemon/power_manager.h +++ b/daemon/power_manager.h @@ -19,7 +19,9 @@ #include <memory> +#include <base/files/file_path.h> #include <base/macros.h> +#include <base/time/time.h> #include <nativepower/BnPowerManager.h> #include "system_property_setter.h" @@ -35,6 +37,9 @@ class PowerManager : public BnPowerManager { static const char kRebootPrefix[]; static const char kShutdownPrefix[]; + // Value written to |power_state_path_| to suspend the system to memory. + static const char kPowerStateSuspend[]; + PowerManager(); ~PowerManager() override; @@ -50,6 +55,10 @@ class PowerManager : public BnPowerManager { wake_lock_manager_ = std::move(manager); } + void set_power_state_path_for_testing(const base::FilePath& path) { + power_state_path_ = path; + } + // Initializes the object, returning true on success. bool Init(); @@ -88,6 +97,13 @@ class PowerManager : public BnPowerManager { std::unique_ptr<SystemPropertySetterInterface> property_setter_; std::unique_ptr<WakeLockManagerInterface> wake_lock_manager_; + // Path to sysfs file that can be written to change the power state. + base::FilePath power_state_path_; + + // System uptime (as duration since boot) when userspace was last resumed from + // suspend. Initially unset. + base::TimeDelta last_resume_uptime_; + DISALLOW_COPY_AND_ASSIGN(PowerManager); }; diff --git a/daemon/power_manager_stub.cc b/daemon/power_manager_stub.cc index f8c653e..7e71bcd 100644 --- a/daemon/power_manager_stub.cc +++ b/daemon/power_manager_stub.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <base/format_macros.h> #include <base/logging.h> #include <base/strings/stringprintf.h> #include <nativepower/power_manager_stub.h> @@ -32,6 +33,13 @@ PowerManagerStub::LockInfo::LockInfo(const std::string& tag, package(package), uid(uid) {} +PowerManagerStub::SuspendRequest::SuspendRequest(int64_t event_time_ms, + int reason, + int flags) + : event_time_ms(event_time_ms), + reason(reason), + flags(flags) {} + // static std::string PowerManagerStub::ConstructLockString(const std::string& tag, const std::string& package, @@ -39,6 +47,14 @@ std::string PowerManagerStub::ConstructLockString(const std::string& tag, return base::StringPrintf("%s,%s,%d", tag.c_str(), package.c_str(), uid); } +// static +std::string PowerManagerStub::ConstructSuspendRequestString( + int64_t event_time_ms, + int reason, + int flags) { + return base::StringPrintf("%" PRId64 ",%d,%d", event_time_ms, reason, flags); +} + PowerManagerStub::PowerManagerStub() = default; PowerManagerStub::~PowerManagerStub() = default; @@ -52,6 +68,15 @@ std::string PowerManagerStub::GetLockString(const sp<IBinder>& binder) const { return ConstructLockString(info.tag, info.package, info.uid); } +std::string PowerManagerStub::GetSuspendRequestString(size_t index) const { + if (index >= suspend_requests_.size()) + return std::string(); + + const SuspendRequest& request = suspend_requests_[index]; + return ConstructSuspendRequestString(request.event_time_ms, request.reason, + request.flags); +} + status_t PowerManagerStub::acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, @@ -100,6 +125,7 @@ status_t PowerManagerStub::powerHint(int hintId, int data) { status_t PowerManagerStub::goToSleep(int64_t event_time_ms, int reason, int flags) { + suspend_requests_.emplace_back(event_time_ms, reason, flags); return OK; } diff --git a/daemon/power_manager_unittest.cc b/daemon/power_manager_unittest.cc index c09c297..148035c 100644 --- a/daemon/power_manager_unittest.cc +++ b/daemon/power_manager_unittest.cc @@ -14,7 +14,10 @@ * limitations under the License. */ +#include <base/files/file_util.h> +#include <base/files/scoped_temp_dir.h> #include <base/macros.h> +#include <base/sys_info.h> #include <binder/IBinder.h> #include <binder/IInterface.h> #include <binderwrapper/binder_test_base.h> @@ -36,20 +39,45 @@ class PowerManagerTest : public BinderTestBase { interface_(interface_cast<IPowerManager>(power_manager_)), property_setter_(new SystemPropertySetterStub()), wake_lock_manager_(new WakeLockManagerStub()) { + CHECK(temp_dir_.CreateUniqueTempDir()); + + power_state_path_ = temp_dir_.path().Append("power_state"); + power_manager_->set_power_state_path_for_testing(power_state_path_); + ClearPowerState(); + power_manager_->set_property_setter_for_testing( std::unique_ptr<SystemPropertySetterInterface>(property_setter_)); power_manager_->set_wake_lock_manager_for_testing( std::unique_ptr<WakeLockManagerInterface>(wake_lock_manager_)); + CHECK(power_manager_->Init()); } ~PowerManagerTest() override = default; protected: + // Returns the value in |power_state_path_|. + std::string ReadPowerState() { + std::string state; + PCHECK(base::ReadFileToString(power_state_path_, &state)) + << "Failed to read " << power_state_path_.value(); + return state; + } + + // Clears |power_state_path_|. + void ClearPowerState() { + PCHECK(base::WriteFile(power_state_path_, "", 0) == 0) + << "Failed to write " << power_state_path_.value(); + } + + base::ScopedTempDir temp_dir_; sp<PowerManager> power_manager_; sp<IPowerManager> interface_; SystemPropertySetterStub* property_setter_; // Owned by |power_manager_|. WakeLockManagerStub* wake_lock_manager_; // Owned by |power_manager_|. + // File under |temp_dir_| used in place of /sys/power/state. + base::FilePath power_state_path_; + private: DISALLOW_COPY_AND_ASSIGN(PowerManagerTest); }; @@ -69,6 +97,26 @@ TEST_F(PowerManagerTest, AcquireAndReleaseWakeLock) { EXPECT_TRUE(wake_lock_manager_->request_binders().empty()); } +TEST_F(PowerManagerTest, GoToSleep) { + EXPECT_EQ("", ReadPowerState()); + + const int kStartTime = base::SysInfo::Uptime(); + EXPECT_EQ(OK, + interface_->goToSleep(kStartTime, 0 /* reason */, 0 /* flags */)); + EXPECT_EQ(PowerManager::kPowerStateSuspend, ReadPowerState()); + + // A request with a timestamp preceding the last resume should be ignored. + ClearPowerState(); + EXPECT_EQ(BAD_VALUE, interface_->goToSleep(kStartTime - 1, 0, 0)); + EXPECT_EQ("", ReadPowerState()); + + // A second attempt with a timestamp occurring after the last + // resume should be honored. + ClearPowerState(); + EXPECT_EQ(OK, interface_->goToSleep(base::SysInfo::Uptime(), 0, 0)); + EXPECT_EQ(PowerManager::kPowerStateSuspend, ReadPowerState()); +} + TEST_F(PowerManagerTest, Reboot) { EXPECT_EQ(OK, interface_->reboot(false, String16(), false)); EXPECT_EQ(PowerManager::kRebootPrefix, diff --git a/example/power_example.cc b/example/power_example.cc index 68134d7..2964c44 100644 --- a/example/power_example.cc +++ b/example/power_example.cc @@ -21,6 +21,8 @@ #include <base/at_exit.h> #include <base/logging.h> #include <base/message_loop/message_loop.h> +#include <base/sys_info.h> +#include <base/time/time.h> #include <binderwrapper/binder_wrapper.h> #include <chromeos/flag_helper.h> #include <nativepower/power_manager_client.h> @@ -35,7 +37,8 @@ const int kWakeLockSleepSec = 5; int main(int argc, char *argv[]) { DEFINE_string(action, "", - "Action to perform (\"reboot\", \"shut_down\", \"wake_lock\")"); + "Action to perform (\"reboot\", \"shut_down\", \"suspend\", " + "\"wake_lock\")"); chromeos::FlagHelper::Init(argc, argv, "Example power-management client."); logging::InitLogging(logging::LoggingSettings()); @@ -52,6 +55,11 @@ int main(int argc, char *argv[]) { } else if (FLAGS_action == "shut_down") { LOG(INFO) << "Requesting shutdown"; CHECK(client.ShutDown(android::ShutdownReason::DEFAULT)); + } else if (FLAGS_action == "suspend") { + LOG(INFO) << "Requesting suspend"; + CHECK(client.Suspend( + base::TimeDelta::FromMilliseconds(base::SysInfo::Uptime()), + android::SuspendReason::APPLICATION, 0 /* flags */)); } else if (FLAGS_action == "wake_lock") { LOG(INFO) << "Creating wake lock"; std::unique_ptr<android::WakeLock> lock( diff --git a/include/nativepower/power_manager_client.h b/include/nativepower/power_manager_client.h index a7053c7..2b683da 100644 --- a/include/nativepower/power_manager_client.h +++ b/include/nativepower/power_manager_client.h @@ -18,12 +18,30 @@ #include <base/macros.h> #include <base/memory/weak_ptr.h> +#include <base/time/time.h> #include <nativepower/wake_lock.h> #include <powermanager/IPowerManager.h> #include <utils/StrongPointer.h> namespace android { +// Reasons that can be passed to PowerManagerClient::Suspend(). +enum class SuspendReason { + // These values must match the ones in android.os.PowerManager. + APPLICATION = 0, + DEVICE_ADMIN = 1, + TIMEOUT = 2, + LID_SWITCH = 3, + POWER_BUTTON = 4, + HDMI = 5, + SLEEP_BUTTON = 6, +}; + +enum class SuspendFlags { + // Corresponds to GO_TO_SLEEP_FLAG_NO_DOZE in android.os.PowerManager. + NO_DOZE = 1 << 0, +}; + // Reasons that can be passed to PowerManagerClient::ShutDown(). enum class ShutdownReason { DEFAULT, @@ -58,7 +76,18 @@ class PowerManagerClient { std::unique_ptr<WakeLock> CreateWakeLock(const std::string& tag, const std::string& package); - // Shuts down or reboots the system. + // Suspends the system immediately, returning true on success. + // + // |event_uptime| contains the time since the system was booted (e.g. + // base::TimeDelta::FromMilliseconds(base::SysInfo::Uptime())) of the event + // that triggered the suspend request. It is used to avoid acting on stale + // suspend requests that are sent before the currently-active suspend request + // completes. + // |reason| is currently only used by android.view.WindowManagerPolicy. + // |flags| is a bitfield of SuspendFlag values. + bool Suspend(base::TimeDelta event_uptime, SuspendReason reason, int flags); + + // Shuts down or reboots the system, returning true on success. bool ShutDown(ShutdownReason reason); bool Reboot(RebootReason reason); diff --git a/include/nativepower/power_manager_stub.h b/include/nativepower/power_manager_stub.h index 966407c..d31c130 100644 --- a/include/nativepower/power_manager_stub.h +++ b/include/nativepower/power_manager_stub.h @@ -35,8 +35,14 @@ class PowerManagerStub : public BnPowerManager { const std::string& package, int uid); - size_t num_locks() const { return locks_.size(); } + // Constructs a string that can be compared with one returned by + // GetSuspendRequestString(). + static std::string ConstructSuspendRequestString(int64_t event_time_ms, + int reason, + int flags); + size_t num_locks() const { return locks_.size(); } + size_t num_suspend_requests() const { return suspend_requests_.size(); } const std::vector<std::string>& reboot_reasons() const { return reboot_reasons_; } @@ -48,6 +54,9 @@ class PowerManagerStub : public BnPowerManager { // string if no lock is present. std::string GetLockString(const sp<IBinder>& binder) const; + // Returns a string describing position |index| in |suspend_requests_|. + std::string GetSuspendRequestString(size_t index) const; + // BnPowerManager: status_t acquireWakeLock(int flags, const sp<IBinder>& lock, @@ -90,9 +99,22 @@ class PowerManagerStub : public BnPowerManager { int uid; }; + // Details about a request passed to goToSleep(). + struct SuspendRequest { + SuspendRequest(int64 uptime_ms, int reason, int flags); + + int64 event_time_ms; + int reason; + int flags; + }; + using LockInfoMap = std::map<sp<IBinder>, LockInfo>; LockInfoMap locks_; + // Information about calls to goToSleep(), in the order they were made. + using SuspendRequests = std::vector<SuspendRequest>; + SuspendRequests suspend_requests_; + // Reasons passed to reboot() and shutdown(), in the order in which they were // received. std::vector<std::string> reboot_reasons_; |