system/hardware/interfaces
Revision | ec036db0798299bd6bf9d38dfaf48d0736ce0f50 (tree) |
---|---|
Time | 2018-08-17 12:30:43 |
Author | Tri Vo <trong@goog...> |
Commiter | android-build-merger |
System suspend HAL implementation. am: 180813bea6
am: b5cfa06fbd
Change-Id: Id33e2dde5ac8b482c04608434f83f592033e3ad6
@@ -0,0 +1,59 @@ | ||
1 | +// Copyright (C) 2018 The Android Open Source Project | |
2 | +// | |
3 | +// Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | +// you may not use this file except in compliance with the License. | |
5 | +// You may obtain a copy of the License at | |
6 | +// | |
7 | +// http://www.apache.org/licenses/LICENSE-2.0 | |
8 | +// | |
9 | +// Unless required by applicable law or agreed to in writing, software | |
10 | +// distributed under the License is distributed on an "AS IS" BASIS, | |
11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | +// See the License for the specific language governing permissions and | |
13 | +// limitations under the License. | |
14 | + | |
15 | +cc_defaults { | |
16 | + name: "system_suspend_defaults", | |
17 | + shared_libs: [ | |
18 | + "libbase", | |
19 | + "libcutils", | |
20 | + "libhidlbase", | |
21 | + "libhidltransport", | |
22 | + "libhwbinder", | |
23 | + "liblog", | |
24 | + "libutils", | |
25 | + ], | |
26 | + cflags: [ | |
27 | + "-Wall", | |
28 | + "-Werror", | |
29 | + ], | |
30 | + cpp_std: "c++17", | |
31 | +} | |
32 | + | |
33 | +cc_binary { | |
34 | + name: "android.system.suspend@1.0-service", | |
35 | + relative_install_path: "hw", | |
36 | + defaults: [ | |
37 | + "system_suspend_defaults", | |
38 | + ], | |
39 | + init_rc: ["android.system.suspend@1.0-service.rc"], | |
40 | + vintf_fragments: ["android.system.suspend@1.0-service.xml"], | |
41 | + shared_libs: ["android.system.suspend@1.0"], | |
42 | + srcs: [ | |
43 | + "SystemSuspend.cpp", | |
44 | + "main.cpp", | |
45 | + ], | |
46 | +} | |
47 | + | |
48 | +// Unit tests for ISystemSuspend implementation. | |
49 | +// Do *NOT* use for compliance with *TS. | |
50 | +cc_test { | |
51 | + name: "SystemSuspendV1_0UnitTest", | |
52 | + defaults: ["system_suspend_defaults"], | |
53 | + static_libs: ["android.system.suspend@1.0"], | |
54 | + srcs: [ | |
55 | + "SystemSuspend.cpp", | |
56 | + "SystemSuspendUnitTest.cpp" | |
57 | + ], | |
58 | +} | |
59 | + |
@@ -0,0 +1,140 @@ | ||
1 | +/* | |
2 | + * Copyright 2018 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include "SystemSuspend.h" | |
18 | + | |
19 | +#include <android-base/file.h> | |
20 | +#include <android-base/logging.h> | |
21 | +#include <android-base/strings.h> | |
22 | +#include <hidl/Status.h> | |
23 | + | |
24 | +#include <sys/stat.h> | |
25 | +#include <sys/types.h> | |
26 | + | |
27 | +#include <fcntl.h> | |
28 | +#include <string> | |
29 | +#include <thread> | |
30 | + | |
31 | +using ::android::base::ReadFdToString; | |
32 | +using ::android::base::WriteStringToFd; | |
33 | +using ::android::hardware::Void; | |
34 | +using ::std::string; | |
35 | + | |
36 | +namespace android { | |
37 | +namespace system { | |
38 | +namespace suspend { | |
39 | +namespace V1_0 { | |
40 | + | |
41 | +static const char kSleepState[] = "mem"; | |
42 | + | |
43 | +// This function assumes that data in fd is small enough that it can be read in one go. | |
44 | +// We use this function instead of the ones available in libbase because it doesn't block | |
45 | +// indefinitely when reading from socket streams which are used for testing. | |
46 | +string readFd(int fd) { | |
47 | + char buf[BUFSIZ]; | |
48 | + ssize_t n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf))); | |
49 | + if (n < 0) return ""; | |
50 | + return string{buf, static_cast<size_t>(n)}; | |
51 | +} | |
52 | + | |
53 | +WakeLock::WakeLock(SystemSuspend* systemSuspend) : mSystemSuspend(systemSuspend) { | |
54 | + mSystemSuspend->incSuspendCounter(); | |
55 | +} | |
56 | + | |
57 | +WakeLock::~WakeLock() { | |
58 | + mSystemSuspend->decSuspendCounter(); | |
59 | +} | |
60 | + | |
61 | +SystemSuspend::SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd) | |
62 | + : mCounterLock(), | |
63 | + mCounterCondVar(), | |
64 | + mSuspendCounter(0), | |
65 | + mWakeupCountFd(std::move(wakeupCountFd)), | |
66 | + mStateFd(std::move(stateFd)) {} | |
67 | + | |
68 | +Return<bool> SystemSuspend::enableAutosuspend() { | |
69 | + static bool initialized = false; | |
70 | + if (initialized) { | |
71 | + LOG(ERROR) << "Autosuspend already started."; | |
72 | + return false; | |
73 | + } | |
74 | + | |
75 | + initAutosuspend(); | |
76 | + initialized = true; | |
77 | + return true; | |
78 | +} | |
79 | + | |
80 | +Return<sp<IWakeLock>> SystemSuspend::acquireWakeLock() { | |
81 | + return new WakeLock{this}; | |
82 | +} | |
83 | + | |
84 | +Return<void> SystemSuspend::debug(const hidl_handle& handle, | |
85 | + const hidl_vec<hidl_string>& /* options */) { | |
86 | + if (handle == nullptr || handle->numFds < 1 || handle->data[0] < 0) { | |
87 | + LOG(ERROR) << "no valid fd"; | |
88 | + return Void(); | |
89 | + } | |
90 | + int fd = handle->data[0]; | |
91 | + WriteStringToFd(std::to_string(mSuspendCounter) + "\n", fd); | |
92 | + fsync(fd); | |
93 | + return Void(); | |
94 | +} | |
95 | + | |
96 | +void SystemSuspend::incSuspendCounter() { | |
97 | + auto l = std::lock_guard(mCounterLock); | |
98 | + mSuspendCounter++; | |
99 | +} | |
100 | + | |
101 | +void SystemSuspend::decSuspendCounter() { | |
102 | + auto l = std::lock_guard(mCounterLock); | |
103 | + if (--mSuspendCounter == 0) { | |
104 | + mCounterCondVar.notify_one(); | |
105 | + } | |
106 | +} | |
107 | + | |
108 | +void SystemSuspend::initAutosuspend() { | |
109 | + std::thread autosuspendThread([this] { | |
110 | + while (true) { | |
111 | + lseek(mWakeupCountFd, 0, SEEK_SET); | |
112 | + const string wakeupCount = readFd(mWakeupCountFd); | |
113 | + if (wakeupCount.empty()) { | |
114 | + PLOG(ERROR) << "error reading from /sys/power/wakeup_count"; | |
115 | + continue; | |
116 | + } | |
117 | + | |
118 | + auto l = std::unique_lock(mCounterLock); | |
119 | + mCounterCondVar.wait(l, [this] { return mSuspendCounter == 0; }); | |
120 | + // The mutex is locked and *MUST* remain locked until the end of the scope. Otherwise, | |
121 | + // a WakeLock might be acquired after we check mSuspendCounter and before we write to | |
122 | + // /sys/power/state. | |
123 | + | |
124 | + if (!WriteStringToFd(wakeupCount, mWakeupCountFd)) { | |
125 | + PLOG(VERBOSE) << "error writing from /sys/power/wakeup_count"; | |
126 | + continue; | |
127 | + } | |
128 | + if (!WriteStringToFd(kSleepState, mStateFd)) { | |
129 | + PLOG(VERBOSE) << "error writing to /sys/power/state"; | |
130 | + } | |
131 | + } | |
132 | + }); | |
133 | + autosuspendThread.detach(); | |
134 | + LOG(INFO) << "automatic system suspend enabled"; | |
135 | +} | |
136 | + | |
137 | +} // namespace V1_0 | |
138 | +} // namespace suspend | |
139 | +} // namespace system | |
140 | +} // namespace android |
@@ -0,0 +1,75 @@ | ||
1 | +/* | |
2 | + * Copyright 2018 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef ANDROID_SYSTEM_SYSTEM_SUSPEND_V1_0_H | |
18 | +#define ANDROID_SYSTEM_SYSTEM_SUSPEND_V1_0_H | |
19 | + | |
20 | +#include <android-base/unique_fd.h> | |
21 | +#include <android/system/suspend/1.0/ISystemSuspend.h> | |
22 | + | |
23 | +#include <condition_variable> | |
24 | +#include <mutex> | |
25 | +#include <string> | |
26 | + | |
27 | +namespace android { | |
28 | +namespace system { | |
29 | +namespace suspend { | |
30 | +namespace V1_0 { | |
31 | + | |
32 | +using ::android::base::unique_fd; | |
33 | +using ::android::hardware::hidl_handle; | |
34 | +using ::android::hardware::hidl_string; | |
35 | +using ::android::hardware::hidl_vec; | |
36 | +using ::android::hardware::Return; | |
37 | + | |
38 | +class SystemSuspend; | |
39 | + | |
40 | +std::string readFd(int fd); | |
41 | + | |
42 | +class WakeLock : public IWakeLock { | |
43 | + public: | |
44 | + WakeLock(SystemSuspend* systemSuspend); | |
45 | + ~WakeLock(); | |
46 | + | |
47 | + private: | |
48 | + SystemSuspend* mSystemSuspend; | |
49 | +}; | |
50 | + | |
51 | +class SystemSuspend : public ISystemSuspend { | |
52 | + public: | |
53 | + SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd); | |
54 | + Return<bool> enableAutosuspend() override; | |
55 | + Return<sp<IWakeLock>> acquireWakeLock() override; | |
56 | + Return<void> debug(const hidl_handle& handle, const hidl_vec<hidl_string>& options) override; | |
57 | + void incSuspendCounter(); | |
58 | + void decSuspendCounter(); | |
59 | + | |
60 | + private: | |
61 | + void initAutosuspend(); | |
62 | + | |
63 | + std::mutex mCounterLock; | |
64 | + std::condition_variable mCounterCondVar; | |
65 | + uint32_t mSuspendCounter; | |
66 | + unique_fd mWakeupCountFd; | |
67 | + unique_fd mStateFd; | |
68 | +}; | |
69 | + | |
70 | +} // namespace V1_0 | |
71 | +} // namespace suspend | |
72 | +} // namespace system | |
73 | +} // namespace android | |
74 | + | |
75 | +#endif // ANDROID_SYSTEM_SYSTEM_SUSPEND_V1_0_H |
@@ -0,0 +1,214 @@ | ||
1 | +/* | |
2 | + * Copyright 2018 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include "SystemSuspend.h" | |
18 | + | |
19 | +#include <android-base/file.h> | |
20 | +#include <android-base/logging.h> | |
21 | +#include <android-base/unique_fd.h> | |
22 | +#include <cutils/native_handle.h> | |
23 | +#include <gtest/gtest.h> | |
24 | +#include <hidl/HidlTransportSupport.h> | |
25 | + | |
26 | +#include <sys/poll.h> | |
27 | +#include <sys/socket.h> | |
28 | +#include <sys/types.h> | |
29 | + | |
30 | +#include <chrono> | |
31 | +#include <csignal> | |
32 | +#include <cstdlib> | |
33 | +#include <future> | |
34 | +#include <string> | |
35 | +#include <thread> | |
36 | + | |
37 | +using android::sp; | |
38 | +using android::base::Socketpair; | |
39 | +using android::base::unique_fd; | |
40 | +using android::base::WriteStringToFd; | |
41 | +using android::hardware::configureRpcThreadpool; | |
42 | +using android::hardware::joinRpcThreadpool; | |
43 | +using android::hardware::Return; | |
44 | +using android::hardware::Void; | |
45 | +using android::system::suspend::V1_0::ISystemSuspend; | |
46 | +using android::system::suspend::V1_0::IWakeLock; | |
47 | +using android::system::suspend::V1_0::readFd; | |
48 | +using android::system::suspend::V1_0::SystemSuspend; | |
49 | + | |
50 | +namespace android { | |
51 | + | |
52 | +static constexpr char kServiceName[] = "TestService"; | |
53 | + | |
54 | +static bool isReadBlocked(int fd) { | |
55 | + struct pollfd pfd { | |
56 | + .fd = fd, .events = POLLIN, | |
57 | + }; | |
58 | + int timeout_ms = 20; | |
59 | + return poll(&pfd, 1, timeout_ms) == 0; | |
60 | +} | |
61 | + | |
62 | +class SystemSuspendTestEnvironment : public ::testing::Environment { | |
63 | + public: | |
64 | + using Env = SystemSuspendTestEnvironment; | |
65 | + static Env* Instance() { | |
66 | + static Env* instance = new Env{}; | |
67 | + return instance; | |
68 | + } | |
69 | + | |
70 | + SystemSuspendTestEnvironment() { | |
71 | + Socketpair(SOCK_STREAM, &wakeupCountFds[0], &wakeupCountFds[1]); | |
72 | + Socketpair(SOCK_STREAM, &stateFds[0], &stateFds[1]); | |
73 | + } | |
74 | + | |
75 | + void registerTestService() { | |
76 | + std::thread testService([this] { | |
77 | + configureRpcThreadpool(1, true /* callerWillJoin */); | |
78 | + sp<ISystemSuspend> suspend = | |
79 | + new SystemSuspend(std::move(wakeupCountFds[1]), std::move(stateFds[1])); | |
80 | + status_t status = suspend->registerAsService(kServiceName); | |
81 | + if (android::OK != status) { | |
82 | + LOG(FATAL) << "Unable to register service: " << status; | |
83 | + } | |
84 | + joinRpcThreadpool(); | |
85 | + }); | |
86 | + testService.detach(); | |
87 | + } | |
88 | + | |
89 | + virtual void SetUp() { | |
90 | + registerTestService(); | |
91 | + ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, kServiceName); | |
92 | + sp<ISystemSuspend> suspendService = ISystemSuspend::getService(kServiceName); | |
93 | + ASSERT_NE(suspendService, nullptr) << "failed to get suspend service"; | |
94 | + ASSERT_EQ(suspendService->enableAutosuspend(), true) << "failed to start autosuspend"; | |
95 | + } | |
96 | + | |
97 | + unique_fd wakeupCountFds[2]; | |
98 | + unique_fd stateFds[2]; | |
99 | +}; | |
100 | + | |
101 | +class SystemSuspendTest : public ::testing::Test { | |
102 | + public: | |
103 | + virtual void SetUp() override { | |
104 | + ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, kServiceName); | |
105 | + suspendService = ISystemSuspend::getService(kServiceName); | |
106 | + ASSERT_NE(suspendService, nullptr) << "failed to get suspend service"; | |
107 | + | |
108 | + auto* environment = SystemSuspendTestEnvironment::Instance(); | |
109 | + wakeupCountFd = environment->wakeupCountFds[0]; | |
110 | + stateFd = environment->stateFds[0]; | |
111 | + | |
112 | + // SystemSuspend HAL should not have written back to wakeupCountFd or stateFd yet. | |
113 | + ASSERT_TRUE(isReadBlocked(wakeupCountFd)); | |
114 | + ASSERT_TRUE(isReadBlocked(stateFd)); | |
115 | + } | |
116 | + | |
117 | + virtual void TearDown() override { | |
118 | + if (!isReadBlocked(wakeupCountFd)) readFd(wakeupCountFd); | |
119 | + if (!isReadBlocked(stateFd)) readFd(stateFd).empty(); | |
120 | + ASSERT_TRUE(isReadBlocked(wakeupCountFd)); | |
121 | + ASSERT_TRUE(isReadBlocked(stateFd)); | |
122 | + } | |
123 | + | |
124 | + void unblockSystemSuspendFromWakeupCount() { | |
125 | + std::string wakeupCount = std::to_string(rand()); | |
126 | + ASSERT_TRUE(WriteStringToFd(wakeupCount, wakeupCountFd)); | |
127 | + } | |
128 | + | |
129 | + bool isSystemSuspendBlocked() { return isReadBlocked(stateFd); } | |
130 | + | |
131 | + sp<ISystemSuspend> suspendService; | |
132 | + int stateFd; | |
133 | + int wakeupCountFd; | |
134 | +}; | |
135 | + | |
136 | +// Tests that autosuspend thread can only be enabled once. | |
137 | +TEST_F(SystemSuspendTest, OnlyOneEnableAutosuspend) { | |
138 | + ASSERT_EQ(suspendService->enableAutosuspend(), false); | |
139 | +} | |
140 | + | |
141 | +TEST_F(SystemSuspendTest, AutosuspendLoop) { | |
142 | + for (int i = 0; i < 2; i++) { | |
143 | + // Mock value for /sys/power/wakeup_count. | |
144 | + std::string wakeupCount = std::to_string(rand()); | |
145 | + ASSERT_TRUE(WriteStringToFd(wakeupCount, wakeupCountFd)); | |
146 | + ASSERT_EQ(readFd(wakeupCountFd), wakeupCount) | |
147 | + << "wakeup count value written by SystemSuspend is not equal to value given to it"; | |
148 | + ASSERT_EQ(readFd(stateFd), "mem") << "SystemSuspend failed to write correct sleep state."; | |
149 | + } | |
150 | +} | |
151 | + | |
152 | +// Tests that upon WakeLock destruction SystemSuspend HAL is unblocked. | |
153 | +TEST_F(SystemSuspendTest, WakeLockDestructor) { | |
154 | + { | |
155 | + sp<IWakeLock> wl = suspendService->acquireWakeLock(); | |
156 | + ASSERT_NE(wl, nullptr); | |
157 | + unblockSystemSuspendFromWakeupCount(); | |
158 | + ASSERT_TRUE(isSystemSuspendBlocked()); | |
159 | + } | |
160 | + ASSERT_FALSE(isSystemSuspendBlocked()); | |
161 | +} | |
162 | + | |
163 | +// Tests that multiple WakeLocks correctly block SystemSuspend HAL. | |
164 | +TEST_F(SystemSuspendTest, MultipleWakeLocks) { | |
165 | + { | |
166 | + sp<IWakeLock> wl1 = suspendService->acquireWakeLock(); | |
167 | + ASSERT_NE(wl1, nullptr); | |
168 | + ASSERT_TRUE(isSystemSuspendBlocked()); | |
169 | + unblockSystemSuspendFromWakeupCount(); | |
170 | + { | |
171 | + sp<IWakeLock> wl2 = suspendService->acquireWakeLock(); | |
172 | + ASSERT_NE(wl2, nullptr); | |
173 | + ASSERT_TRUE(isSystemSuspendBlocked()); | |
174 | + } | |
175 | + ASSERT_TRUE(isSystemSuspendBlocked()); | |
176 | + } | |
177 | + ASSERT_FALSE(isSystemSuspendBlocked()); | |
178 | +} | |
179 | + | |
180 | +// Tests that upon thread deallocation WakeLock is destructed and SystemSuspend HAL is unblocked. | |
181 | +TEST_F(SystemSuspendTest, ThreadCleanup) { | |
182 | + std::thread clientThread([this] { | |
183 | + sp<IWakeLock> wl = suspendService->acquireWakeLock(); | |
184 | + ASSERT_NE(wl, nullptr); | |
185 | + unblockSystemSuspendFromWakeupCount(); | |
186 | + ASSERT_TRUE(isSystemSuspendBlocked()); | |
187 | + }); | |
188 | + clientThread.join(); | |
189 | + ASSERT_FALSE(isSystemSuspendBlocked()); | |
190 | +} | |
191 | + | |
192 | +// Test that binder driver correctly deallocates acquired WakeLocks, even if the client processs | |
193 | +// is terminated without ability to do clean up. | |
194 | +TEST_F(SystemSuspendTest, CleanupOnAbort) { | |
195 | + ASSERT_EXIT( | |
196 | + { | |
197 | + sp<IWakeLock> wl = suspendService->acquireWakeLock(); | |
198 | + ASSERT_NE(wl, nullptr); | |
199 | + std::abort(); | |
200 | + }, | |
201 | + ::testing::KilledBySignal(SIGABRT), ""); | |
202 | + ASSERT_TRUE(isSystemSuspendBlocked()); | |
203 | + unblockSystemSuspendFromWakeupCount(); | |
204 | + ASSERT_FALSE(isSystemSuspendBlocked()); | |
205 | +} | |
206 | + | |
207 | +} // namespace android | |
208 | + | |
209 | +int main(int argc, char** argv) { | |
210 | + setenv("TREBLE_TESTING_OVERRIDE", "true", true); | |
211 | + ::testing::AddGlobalTestEnvironment(android::SystemSuspendTestEnvironment::Instance()); | |
212 | + ::testing::InitGoogleTest(&argc, argv); | |
213 | + return RUN_ALL_TESTS(); | |
214 | +} |
@@ -0,0 +1,4 @@ | ||
1 | +service system_suspend /system/bin/hw/android.system.suspend@1.0-service | |
2 | + class hal | |
3 | + user system | |
4 | + group system |
@@ -0,0 +1,11 @@ | ||
1 | +<manifest version="1.0" type="framework"> | |
2 | + <hal> | |
3 | + <name>android.system.suspend</name> | |
4 | + <transport>hwbinder</transport> | |
5 | + <version>1.0</version> | |
6 | + <interface> | |
7 | + <name>ISystemSuspend</name> | |
8 | + <instance>default</instance> | |
9 | + </interface> | |
10 | + </hal> | |
11 | +</manifest> |
@@ -0,0 +1,44 @@ | ||
1 | +#include "SystemSuspend.h" | |
2 | + | |
3 | +#include <android-base/logging.h> | |
4 | +#include <cutils/native_handle.h> | |
5 | +#include <hidl/HidlTransportSupport.h> | |
6 | + | |
7 | +#include <sys/stat.h> | |
8 | +#include <sys/types.h> | |
9 | + | |
10 | +#include <fcntl.h> | |
11 | +#include <unistd.h> | |
12 | + | |
13 | +using android::sp; | |
14 | +using android::status_t; | |
15 | +using android::base::unique_fd; | |
16 | +using android::hardware::configureRpcThreadpool; | |
17 | +using android::hardware::joinRpcThreadpool; | |
18 | +using android::system::suspend::V1_0::ISystemSuspend; | |
19 | +using android::system::suspend::V1_0::SystemSuspend; | |
20 | + | |
21 | +static constexpr char kSysPowerWakeupCount[] = "/sys/power/wakeup_count"; | |
22 | +static constexpr char kSysPowerState[] = "/sys/power/state"; | |
23 | + | |
24 | +int main() { | |
25 | + unique_fd wakeupCountFd{TEMP_FAILURE_RETRY(open(kSysPowerWakeupCount, O_CLOEXEC | O_RDWR))}; | |
26 | + if (wakeupCountFd < 0) { | |
27 | + PLOG(ERROR) << "error opening " << kSysPowerWakeupCount; | |
28 | + return 1; | |
29 | + } | |
30 | + unique_fd stateFd{TEMP_FAILURE_RETRY(open(kSysPowerState, O_CLOEXEC | O_RDWR))}; | |
31 | + if (stateFd < 0) { | |
32 | + PLOG(ERROR) << "error opening " << kSysPowerState; | |
33 | + return 1; | |
34 | + } | |
35 | + | |
36 | + configureRpcThreadpool(1, true /* callerWillJoin */); | |
37 | + sp<ISystemSuspend> suspend = new SystemSuspend(std::move(wakeupCountFd), std::move(stateFd)); | |
38 | + status_t status = suspend->registerAsService(); | |
39 | + if (android::OK != status) { | |
40 | + LOG(FATAL) << "Unable to register service: " << status; | |
41 | + } | |
42 | + joinRpcThreadpool(); | |
43 | + std::abort(); /* unreachable */ | |
44 | +} |