• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <aidl/android/system/suspend/ISystemSuspend.h>
20 #include <aidl/android/system/suspend/IWakeLock.h>
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 #include <android/binder_manager.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 
30 #include <chrono>
31 #include <string>
32 #include <thread>
33 using namespace std::chrono_literals;
34 
35 using ::aidl::android::system::suspend::ISystemSuspend;
36 using ::aidl::android::system::suspend::IWakeLock;
37 using ::aidl::android::system::suspend::WakeLockType;
38 using ::android::base::Error;
39 using ::android::base::ReadFdToString;
40 using ::android::base::WriteStringToFd;
41 using ::std::string;
42 
43 namespace android {
44 namespace system {
45 namespace suspend {
46 namespace V1_0 {
47 
48 struct SuspendTime {
49     std::chrono::nanoseconds suspendOverhead;
50     std::chrono::nanoseconds suspendTime;
51 };
52 
53 static const char kSleepState[] = "mem";
54 // TODO(b/128923994): we only need /sys/power/wake_[un]lock to export debugging info via
55 // /sys/kernel/debug/wakeup_sources.
56 static constexpr char kSysPowerWakeLock[] = "/sys/power/wake_lock";
57 static constexpr char kSysPowerWakeUnlock[] = "/sys/power/wake_unlock";
58 static constexpr char kUnknownWakeup[] = "unknown";
59 // This is used to disable autosuspend when zygote is restarted
60 // it allows the system to make progress before autosuspend is kicked
61 // NOTE: If the name of this wakelock is changed then also update the name
62 // in rootdir/init.zygote32.rc, rootdir/init.zygote64.rc, and
63 // rootdir/init.zygote64_32.rc
64 static constexpr char kZygoteKernelWakelock[] = "zygote_kwl";
65 
66 // This function assumes that data in fd is small enough that it can be read in one go.
67 // We use this function instead of the ones available in libbase because it doesn't block
68 // indefinitely when reading from socket streams which are used for testing.
readFd(int fd)69 string readFd(int fd) {
70     char buf[BUFSIZ];
71     ssize_t n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)));
72     if (n < 0) return "";
73     return string{buf, static_cast<size_t>(n)};
74 }
75 
readWakeupReasons(int fd)76 static std::vector<std::string> readWakeupReasons(int fd) {
77     std::vector<std::string> wakeupReasons;
78     std::string reasonlines;
79 
80     lseek(fd, 0, SEEK_SET);
81 
82     bool ret = ReadFdToString(fd, &reasonlines);
83     if (!ret)
84         PLOG(ERROR) << "failed to read wakeup reasons";
85 
86     // Return unknown wakeup reason if reading wakeup reasons fails or is empty
87     if (!ret || reasonlines.empty())
88         return {kUnknownWakeup};
89 
90     std::stringstream ss(reasonlines);
91     for (std::string reasonline; std::getline(ss, reasonline);) {
92         reasonline = ::android::base::Trim(reasonline);
93 
94         // Only include non-empty reason lines
95         if (!reasonline.empty()) {
96             wakeupReasons.push_back(reasonline);
97         }
98     }
99 
100     // Empty wakeup reason found. Record as unknown wakeup
101     if (wakeupReasons.empty()) {
102         wakeupReasons.push_back(kUnknownWakeup);
103     }
104 
105     return wakeupReasons;
106 }
107 
108 // reads the suspend overhead and suspend time
109 // Returns 0s if reading the sysfs node fails (unlikely)
readSuspendTime(int fd)110 static struct SuspendTime readSuspendTime(int fd) {
111     std::string content;
112 
113     lseek(fd, 0, SEEK_SET);
114     if (!ReadFdToString(fd, &content)) {
115         LOG(ERROR) << "failed to read suspend time";
116         return {0ns, 0ns};
117     }
118 
119     double suspendOverhead, suspendTime;
120     std::stringstream ss(content);
121     if (!(ss >> suspendOverhead) || !(ss >> suspendTime)) {
122         LOG(ERROR) << "failed to parse suspend time " << content;
123         return {0ns, 0ns};
124     }
125 
126     return {std::chrono::duration_cast<std::chrono::nanoseconds>(
127                 std::chrono::duration<double>(suspendOverhead)),
128             std::chrono::duration_cast<std::chrono::nanoseconds>(
129                 std::chrono::duration<double>(suspendTime))};
130 }
131 
SystemSuspend(unique_fd wakeupCountFd,unique_fd stateFd,unique_fd suspendStatsFd,size_t maxStatsEntries,unique_fd kernelWakelockStatsFd,unique_fd wakeupReasonsFd,unique_fd suspendTimeFd,const SleepTimeConfig & sleepTimeConfig,const sp<SuspendControlService> & controlService,const sp<SuspendControlServiceInternal> & controlServiceInternal,bool useSuspendCounter)132 SystemSuspend::SystemSuspend(unique_fd wakeupCountFd, unique_fd stateFd, unique_fd suspendStatsFd,
133                              size_t maxStatsEntries, unique_fd kernelWakelockStatsFd,
134                              unique_fd wakeupReasonsFd, unique_fd suspendTimeFd,
135                              const SleepTimeConfig& sleepTimeConfig,
136                              const sp<SuspendControlService>& controlService,
137                              const sp<SuspendControlServiceInternal>& controlServiceInternal,
138                              bool useSuspendCounter)
139     : mSuspendCounter(0),
140       mWakeupCountFd(std::move(wakeupCountFd)),
141       mStateFd(std::move(stateFd)),
142       mSuspendStatsFd(std::move(suspendStatsFd)),
143       mSuspendTimeFd(std::move(suspendTimeFd)),
144       kSleepTimeConfig(sleepTimeConfig),
145       mSleepTime(sleepTimeConfig.baseSleepTime),
146       mNumConsecutiveBadSuspends(0),
147       mControlService(controlService),
148       mControlServiceInternal(controlServiceInternal),
149       mStatsList(maxStatsEntries, std::move(kernelWakelockStatsFd)),
150       mWakeupList(maxStatsEntries),
151       mUseSuspendCounter(useSuspendCounter),
152       mWakeLockFd(-1),
153       mWakeUnlockFd(-1),
154       mWakeupReasonsFd(std::move(wakeupReasonsFd)) {
155     mControlServiceInternal->setSuspendService(this);
156 
157     if (!mUseSuspendCounter) {
158         mWakeLockFd.reset(TEMP_FAILURE_RETRY(open(kSysPowerWakeLock, O_CLOEXEC | O_RDWR)));
159         if (mWakeLockFd < 0) {
160             PLOG(ERROR) << "error opening " << kSysPowerWakeLock;
161         }
162     }
163 
164     mWakeUnlockFd.reset(TEMP_FAILURE_RETRY(open(kSysPowerWakeUnlock, O_CLOEXEC | O_RDWR)));
165     if (mWakeUnlockFd < 0) {
166         PLOG(ERROR) << "error opening " << kSysPowerWakeUnlock;
167     }
168 }
169 
enableAutosuspend(const sp<IBinder> & token)170 bool SystemSuspend::enableAutosuspend(const sp<IBinder>& token) {
171     auto tokensLock = std::lock_guard(mAutosuspendClientTokensLock);
172     auto autosuspendLock = std::lock_guard(mAutosuspendLock);
173 
174     // Disable zygote kernel wakelock, since explicitly attempting to
175     // enable autosuspend. This should be done even if autosuspend is
176     // already enabled, since it could be the case that the framework
177     // is restarting and connecting to the existing suspend service.
178     if (!WriteStringToFd(kZygoteKernelWakelock, mWakeUnlockFd)) {
179         PLOG(ERROR) << "error writing " << kZygoteKernelWakelock << " to " << kSysPowerWakeUnlock;
180     }
181 
182     bool hasToken = std::find(mAutosuspendClientTokens.begin(), mAutosuspendClientTokens.end(),
183                               token) != mAutosuspendClientTokens.end();
184 
185     if (!hasToken) {
186         mAutosuspendClientTokens.push_back(token);
187     }
188 
189     if (mAutosuspendEnabled) {
190         LOG(ERROR) << "Autosuspend already started.";
191         return false;
192     }
193 
194     mAutosuspendEnabled = true;
195     initAutosuspendLocked();
196     return true;
197 }
198 
disableAutosuspendLocked()199 void SystemSuspend::disableAutosuspendLocked() {
200     mAutosuspendClientTokens.clear();
201     if (mAutosuspendEnabled) {
202         mAutosuspendEnabled = false;
203         mAutosuspendCondVar.notify_all();
204         LOG(INFO) << "automatic system suspend disabled";
205     }
206 }
207 
disableAutosuspend()208 void SystemSuspend::disableAutosuspend() {
209     auto tokensLock = std::lock_guard(mAutosuspendClientTokensLock);
210     auto autosuspendLock = std::lock_guard(mAutosuspendLock);
211     disableAutosuspendLocked();
212 }
213 
checkAutosuspendClientsLivenessLocked()214 void SystemSuspend::checkAutosuspendClientsLivenessLocked() {
215     // Ping autosuspend client tokens, remove any dead tokens from the list.
216     // mAutosuspendLock must not be held when calling this, as that could lead to a deadlock
217     // if pingBinder() can't be processed by system_server because it's Binder thread pool is
218     // exhausted and blocked on acquire/release wakelock calls.
219     mAutosuspendClientTokens.erase(
220         std::remove_if(mAutosuspendClientTokens.begin(), mAutosuspendClientTokens.end(),
221                        [](const sp<IBinder>& token) { return token->pingBinder() != OK; }),
222         mAutosuspendClientTokens.end());
223 }
224 
hasAliveAutosuspendTokenLocked()225 bool SystemSuspend::hasAliveAutosuspendTokenLocked() {
226     return !mAutosuspendClientTokens.empty();
227 }
228 
~SystemSuspend(void)229 SystemSuspend::~SystemSuspend(void) {
230     auto tokensLock = std::lock_guard(mAutosuspendClientTokensLock);
231     auto autosuspendLock = std::unique_lock(mAutosuspendLock);
232 
233     // signal autosuspend thread to shut down
234     disableAutosuspendLocked();
235 
236     // wait for autosuspend thread to exit
237     mAutosuspendCondVar.wait_for(autosuspendLock, 100ms, [this]() REQUIRES(mAutosuspendLock) {
238         return !mAutosuspendThreadCreated;
239     });
240 }
241 
forceSuspend()242 bool SystemSuspend::forceSuspend() {
243     //  We are forcing the system to suspend. This particular call ignores all
244     //  existing wakelocks (full or partial). It does not cancel the wakelocks
245     //  or reset mSuspendCounter, it just ignores them.  When the system
246     //  returns from suspend, the wakelocks and SuspendCounter will not have
247     //  changed.
248     auto autosuspendLock = std::unique_lock(mAutosuspendLock);
249     bool success = WriteStringToFd(kSleepState, mStateFd);
250     autosuspendLock.unlock();
251 
252     if (!success) {
253         PLOG(VERBOSE) << "error writing to /sys/power/state for forceSuspend";
254     }
255     return success;
256 }
257 
incSuspendCounter(const string & name)258 void SystemSuspend::incSuspendCounter(const string& name) {
259     auto l = std::lock_guard(mAutosuspendLock);
260     if (mUseSuspendCounter) {
261         mSuspendCounter++;
262     } else {
263         if (!WriteStringToFd(name, mWakeLockFd)) {
264             PLOG(ERROR) << "error writing " << name << " to " << kSysPowerWakeLock;
265         }
266     }
267 }
268 
decSuspendCounter(const string & name)269 void SystemSuspend::decSuspendCounter(const string& name) {
270     auto l = std::lock_guard(mAutosuspendLock);
271     if (mUseSuspendCounter) {
272         if (--mSuspendCounter == 0) {
273             mAutosuspendCondVar.notify_one();
274         }
275     } else {
276         if (!WriteStringToFd(name, mWakeUnlockFd)) {
277             PLOG(ERROR) << "error writing " << name << " to " << kSysPowerWakeUnlock;
278         }
279     }
280 }
281 
reopenFileUsingFd(const int fd,const int permission)282 unique_fd SystemSuspend::reopenFileUsingFd(const int fd, const int permission) {
283     string filePath = android::base::StringPrintf("/proc/self/fd/%d", fd);
284 
285     unique_fd tempFd{TEMP_FAILURE_RETRY(open(filePath.c_str(), permission))};
286     if (tempFd < 0) {
287         PLOG(ERROR) << "SystemSuspend: Error opening file, using path: " << filePath;
288         return unique_fd(-1);
289     }
290     return tempFd;
291 }
292 
initAutosuspendLocked()293 void SystemSuspend::initAutosuspendLocked() {
294     if (mAutosuspendThreadCreated) {
295         LOG(INFO) << "Autosuspend thread already started.";
296         return;
297     }
298 
299     std::thread autosuspendThread([this] {
300         auto autosuspendLock = std::unique_lock(mAutosuspendLock);
301         bool shouldSleep = true;
302 
303         while (true) {
304             {
305                 base::ScopedLockAssertion autosuspendLocked(mAutosuspendLock);
306 
307                 if (!mAutosuspendEnabled) {
308                     mAutosuspendThreadCreated = false;
309                     return;
310                 }
311                 // If we got here by a failed write to /sys/power/wakeup_count; don't sleep
312                 // since we didn't attempt to suspend on the last cycle of this loop.
313                 if (shouldSleep) {
314                     mAutosuspendCondVar.wait_for(
315                         autosuspendLock, mSleepTime,
316                         [this]() REQUIRES(mAutosuspendLock) { return !mAutosuspendEnabled; });
317                 }
318 
319                 if (!mAutosuspendEnabled) continue;
320                 autosuspendLock.unlock();
321             }
322 
323             lseek(mWakeupCountFd, 0, SEEK_SET);
324             string wakeupCount = readFd(mWakeupCountFd);
325 
326             {
327                 autosuspendLock.lock();
328                 base::ScopedLockAssertion autosuspendLocked(mAutosuspendLock);
329 
330                 if (wakeupCount.empty()) {
331                     PLOG(ERROR) << "error reading from /sys/power/wakeup_count";
332                     continue;
333                 }
334 
335                 shouldSleep = false;
336 
337                 mAutosuspendCondVar.wait(autosuspendLock, [this]() REQUIRES(mAutosuspendLock) {
338                     return mSuspendCounter == 0 || !mAutosuspendEnabled;
339                 });
340 
341                 if (!mAutosuspendEnabled) continue;
342                 autosuspendLock.unlock();
343             }
344 
345             bool success;
346             {
347                 auto tokensLock = std::lock_guard(mAutosuspendClientTokensLock);
348                 // TODO: Clean up client tokens after soaking the new approach
349                 // checkAutosuspendClientsLivenessLocked();
350 
351                 autosuspendLock.lock();
352                 base::ScopedLockAssertion autosuspendLocked(mAutosuspendLock);
353 
354                 if (!hasAliveAutosuspendTokenLocked()) {
355                     disableAutosuspendLocked();
356                     continue;
357                 }
358 
359                 // Check suspend counter hasn't increased while checking client liveness
360                 if (mSuspendCounter > 0) {
361                     continue;
362                 }
363 
364                 // The mutex is locked and *MUST* remain locked until we write to /sys/power/state.
365                 // Otherwise, a WakeLock might be acquired after we check mSuspendCounter and before
366                 // we write to /sys/power/state.
367 
368                 if (!WriteStringToFd(wakeupCount, mWakeupCountFd)) {
369                     PLOG(VERBOSE) << "error writing to /sys/power/wakeup_count";
370                     continue;
371                 }
372                 success = WriteStringToFd(kSleepState, mStateFd);
373                 shouldSleep = true;
374 
375                 autosuspendLock.unlock();
376             }
377 
378             if (!success) {
379                 PLOG(VERBOSE) << "error writing to /sys/power/state";
380             }
381 
382             struct SuspendTime suspendTime = readSuspendTime(mSuspendTimeFd);
383             updateSleepTime(success, suspendTime);
384 
385             std::vector<std::string> wakeupReasons = readWakeupReasons(mWakeupReasonsFd);
386             if (wakeupReasons == std::vector<std::string>({kUnknownWakeup})) {
387                 LOG(INFO) << "Unknown/empty wakeup reason. Re-opening wakeup_reason file.";
388 
389                 mWakeupReasonsFd =
390                     std::move(reopenFileUsingFd(mWakeupReasonsFd.get(), O_CLOEXEC | O_RDONLY));
391             }
392             mWakeupList.update(wakeupReasons);
393 
394             mControlService->notifyWakeup(success, wakeupReasons);
395 
396             // Take the lock before returning to the start of the loop
397             autosuspendLock.lock();
398         }
399     });
400     autosuspendThread.detach();
401     mAutosuspendThreadCreated = true;
402     LOG(INFO) << "automatic system suspend enabled";
403 }
404 
405 /**
406  * Updates sleep time depending on the result of suspend attempt.
407  * Time (in milliseconds) between suspend attempts is described the formula
408  * t[n] = { B, 0 < n <= N
409  *        { min(B * (S**(n - N)), M), n > N
410  * where:
411  *   n is the number of consecutive bad suspend attempts,
412  *   B = kBaseSleepTime,
413  *   N = kSuspendBackoffThreshold,
414  *   S = kSleepTimeScaleFactor,
415  *   M = kMaxSleepTime
416  *
417  * kFailedSuspendBackoffEnabled determines whether a failed suspend is counted as a bad suspend
418  *
419  * kShortSuspendBackoffEnabled determines whether a suspend whose duration
420  * t < kShortSuspendThreshold is counted as a bad suspend
421  */
updateSleepTime(bool success,const struct SuspendTime & suspendTime)422 void SystemSuspend::updateSleepTime(bool success, const struct SuspendTime& suspendTime) {
423     std::scoped_lock lock(mSuspendInfoLock);
424     mSuspendInfo.suspendAttemptCount++;
425     mSuspendInfo.sleepTimeMillis +=
426         std::chrono::round<std::chrono::milliseconds>(mSleepTime).count();
427 
428     bool shortSuspend = success && (suspendTime.suspendTime > 0ns) &&
429                         (suspendTime.suspendTime < kSleepTimeConfig.shortSuspendThreshold);
430 
431     bool badSuspend = (kSleepTimeConfig.failedSuspendBackoffEnabled && !success) ||
432                       (kSleepTimeConfig.shortSuspendBackoffEnabled && shortSuspend);
433 
434     auto suspendTimeMillis =
435         std::chrono::round<std::chrono::milliseconds>(suspendTime.suspendTime).count();
436     auto suspendOverheadMillis =
437         std::chrono::round<std::chrono::milliseconds>(suspendTime.suspendOverhead).count();
438 
439     if (success) {
440         mSuspendInfo.suspendOverheadTimeMillis += suspendOverheadMillis;
441         mSuspendInfo.suspendTimeMillis += suspendTimeMillis;
442     } else {
443         mSuspendInfo.failedSuspendCount++;
444         mSuspendInfo.failedSuspendOverheadTimeMillis += suspendOverheadMillis;
445     }
446 
447     if (shortSuspend) {
448         mSuspendInfo.shortSuspendCount++;
449         mSuspendInfo.shortSuspendTimeMillis += suspendTimeMillis;
450     }
451 
452     if (!badSuspend) {
453         mNumConsecutiveBadSuspends = 0;
454         mSleepTime = kSleepTimeConfig.baseSleepTime;
455         return;
456     }
457 
458     // Suspend attempt was bad (failed or short suspend)
459     if (mNumConsecutiveBadSuspends >= kSleepTimeConfig.backoffThreshold) {
460         if (mNumConsecutiveBadSuspends == kSleepTimeConfig.backoffThreshold) {
461             mSuspendInfo.newBackoffCount++;
462         } else {
463             mSuspendInfo.backoffContinueCount++;
464         }
465 
466         mSleepTime = std::min(std::chrono::round<std::chrono::milliseconds>(
467                                   mSleepTime * kSleepTimeConfig.sleepTimeScaleFactor),
468                               kSleepTimeConfig.maxSleepTime);
469     }
470 
471     mNumConsecutiveBadSuspends++;
472 }
473 
updateWakeLockStatOnAcquire(const std::string & name,int pid)474 void SystemSuspend::updateWakeLockStatOnAcquire(const std::string& name, int pid) {
475     // Update the stats first so that the stat time is right after
476     // suspend counter being incremented.
477     mStatsList.updateOnAcquire(name, pid);
478     mControlService->notifyWakelock(name, true);
479 }
480 
updateWakeLockStatOnRelease(const std::string & name,int pid)481 void SystemSuspend::updateWakeLockStatOnRelease(const std::string& name, int pid) {
482     // Update the stats first so that the stat time is right after
483     // suspend counter being decremented.
484     mStatsList.updateOnRelease(name, pid);
485     mControlService->notifyWakelock(name, false);
486 }
487 
getStatsList() const488 const WakeLockEntryList& SystemSuspend::getStatsList() const {
489     return mStatsList;
490 }
491 
updateStatsNow()492 void SystemSuspend::updateStatsNow() {
493     mStatsList.updateNow();
494 }
495 
getSuspendInfo(SuspendInfo * info)496 void SystemSuspend::getSuspendInfo(SuspendInfo* info) {
497     std::scoped_lock lock(mSuspendInfoLock);
498 
499     *info = mSuspendInfo;
500 }
501 
getWakeupList() const502 const WakeupList& SystemSuspend::getWakeupList() const {
503     return mWakeupList;
504 }
505 
506 /**
507  * Returns suspend stats.
508  */
getSuspendStats()509 Result<SuspendStats> SystemSuspend::getSuspendStats() {
510     SuspendStats stats;
511     std::unique_ptr<DIR, decltype(&closedir)> dp(fdopendir(dup(mSuspendStatsFd.get())), &closedir);
512     if (!dp) {
513         return stats;
514     }
515 
516     // rewinddir, else subsequent calls will not get any suspend_stats
517     rewinddir(dp.get());
518 
519     struct dirent* de;
520 
521     // Grab a wakelock before reading suspend stats, to ensure a consistent snapshot.
522     const std::string suspendInstance = std::string() + ISystemSuspend::descriptor + "/default";
523     auto suspendService = ISystemSuspend::fromBinder(
524         ndk::SpAIBinder(AServiceManager_checkService(suspendInstance.c_str())));
525 
526     std::shared_ptr<IWakeLock> wl = nullptr;
527     if (suspendService) {
528         auto status =
529             suspendService->acquireWakeLock(WakeLockType::PARTIAL, "suspend_stats_lock", &wl);
530     }
531 
532     while ((de = readdir(dp.get()))) {
533         std::string statName(de->d_name);
534         if ((statName == ".") || (statName == "..")) {
535             continue;
536         }
537 
538         unique_fd statFd{TEMP_FAILURE_RETRY(
539             openat(mSuspendStatsFd.get(), statName.c_str(), O_CLOEXEC | O_RDONLY))};
540         if (statFd < 0) {
541             return Error() << "Failed to open " << statName;
542         }
543 
544         std::string valStr;
545         if (!ReadFdToString(statFd.get(), &valStr)) {
546             return Error() << "Failed to read " << statName;
547         }
548 
549         // Trim newline
550         valStr.erase(std::remove(valStr.begin(), valStr.end(), '\n'), valStr.end());
551 
552         if (statName == "last_failed_dev") {
553             stats.lastFailedDev = valStr;
554         } else if (statName == "last_failed_step") {
555             stats.lastFailedStep = valStr;
556         } else {
557             int statVal = std::stoi(valStr);
558             if (statName == "success") {
559                 stats.success = statVal;
560             } else if (statName == "fail") {
561                 stats.fail = statVal;
562             } else if (statName == "failed_freeze") {
563                 stats.failedFreeze = statVal;
564             } else if (statName == "failed_prepare") {
565                 stats.failedPrepare = statVal;
566             } else if (statName == "failed_suspend") {
567                 stats.failedSuspend = statVal;
568             } else if (statName == "failed_suspend_late") {
569                 stats.failedSuspendLate = statVal;
570             } else if (statName == "failed_suspend_noirq") {
571                 stats.failedSuspendNoirq = statVal;
572             } else if (statName == "failed_resume") {
573                 stats.failedResume = statVal;
574             } else if (statName == "failed_resume_early") {
575                 stats.failedResumeEarly = statVal;
576             } else if (statName == "failed_resume_noirq") {
577                 stats.failedResumeNoirq = statVal;
578             } else if (statName == "last_failed_errno") {
579                 stats.lastFailedErrno = statVal;
580             }
581         }
582     }
583 
584     return stats;
585 }
586 
getSleepTime() const587 std::chrono::milliseconds SystemSuspend::getSleepTime() const {
588     return mSleepTime;
589 }
590 
591 }  // namespace V1_0
592 }  // namespace suspend
593 }  // namespace system
594 }  // namespace android
595