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