aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Erat <derat@google.com>2015-10-06 16:54:12 -0600
committerDaniel Erat <derat@google.com>2015-10-07 17:13:14 -0600
commite4d4a81d6b56d66970f0332c6db2b898f60510c8 (patch)
tree48e8e7c47f22f6f487c20eef49056c6ac18e9022
parentc2a4b05a931fbf3ecbf1915049b549b33dae8ba5 (diff)
downloadnativepower-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.cc13
-rw-r--r--client/power_manager_client_unittest.cc14
-rw-r--r--daemon/power_manager.cc39
-rw-r--r--daemon/power_manager.h16
-rw-r--r--daemon/power_manager_stub.cc26
-rw-r--r--daemon/power_manager_unittest.cc48
-rw-r--r--example/power_example.cc10
-rw-r--r--include/nativepower/power_manager_client.h31
-rw-r--r--include/nativepower/power_manager_stub.h24
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_;