Merge "Speed up kernel wakelock polling" into main
diff --git a/suspend/1.0/default/Android.bp b/suspend/1.0/default/Android.bp
index d203698..6652b6d 100644
--- a/suspend/1.0/default/Android.bp
+++ b/suspend/1.0/default/Android.bp
@@ -19,6 +19,9 @@
 
 cc_defaults {
     name: "system_suspend_defaults",
+    defaults: [
+        "aconfig_lib_cc_static_link.defaults",
+    ],
     shared_libs: [
         "libbase",
         "libbinder",
@@ -27,6 +30,10 @@
         "libhidlbase",
         "liblog",
         "libutils",
+        "server_configurable_flags",
+    ],
+    static_libs: [
+        "suspend_service_flags_c_lib",
     ],
     cflags: [
         "-Wall",
@@ -206,3 +213,15 @@
         "fuzzers/SuspendServiceInternalFuzzer.cpp",
     ],
 }
+
+aconfig_declarations {
+    name: "suspend_service_flags",
+    package: "suspend_service.flags",
+    container: "system",
+    srcs: ["flags.aconfig"],
+}
+
+cc_aconfig_library {
+    name: "suspend_service_flags_c_lib",
+    aconfig_declarations: "suspend_service_flags",
+}
diff --git a/suspend/1.0/default/SuspendControlService.cpp b/suspend/1.0/default/SuspendControlService.cpp
index 43f17db..3e88faf 100644
--- a/suspend/1.0/default/SuspendControlService.cpp
+++ b/suspend/1.0/default/SuspendControlService.cpp
@@ -183,7 +183,22 @@
     }
 
     suspendService->updateStatsNow();
-    suspendService->getStatsList().getWakeLockStats(_aidl_return);
+    suspendService->getStatsList().getWakeLockStats(
+        BnSuspendControlServiceInternal::WAKE_LOCK_INFO_ALL_FIELDS, _aidl_return);
+
+    return binder::Status::ok();
+}
+
+binder::Status SuspendControlServiceInternal::getWakeLockStatsFiltered(
+    int wakeLockInfoFieldBitMask, std::vector<WakeLockInfo>* _aidl_return) {
+    const auto suspendService = mSuspend.promote();
+    if (!suspendService) {
+        return binder::Status::fromExceptionCode(binder::Status::Exception::EX_NULL_POINTER,
+                                                 String8("Null reference to suspendService"));
+    }
+
+    suspendService->updateStatsNow();
+    suspendService->getStatsList().getWakeLockStats(wakeLockInfoFieldBitMask, _aidl_return);
 
     return binder::Status::ok();
 }
diff --git a/suspend/1.0/default/SuspendControlService.h b/suspend/1.0/default/SuspendControlService.h
index 7d7e0ae..b6e34ff 100644
--- a/suspend/1.0/default/SuspendControlService.h
+++ b/suspend/1.0/default/SuspendControlService.h
@@ -75,6 +75,8 @@
     binder::Status forceSuspend(bool* _aidl_return) override;
     binder::Status getSuspendStats(SuspendInfo* _aidl_return) override;
     binder::Status getWakeLockStats(std::vector<WakeLockInfo>* _aidl_return) override;
+    binder::Status getWakeLockStatsFiltered(int wakeLockInfoFieldBitMask,
+                                            std::vector<WakeLockInfo>* _aidl_return) override;
     binder::Status getWakeupStats(std::vector<WakeupInfo>* _aidl_return) override;
 
     void setSuspendService(const wp<SystemSuspend>& suspend);
diff --git a/suspend/1.0/default/SystemSuspend.cpp b/suspend/1.0/default/SystemSuspend.cpp
index 9493e85..760aea8 100644
--- a/suspend/1.0/default/SystemSuspend.cpp
+++ b/suspend/1.0/default/SystemSuspend.cpp
@@ -28,6 +28,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android/binder_manager.h>
+#include <android/system/suspend/internal/ISuspendControlServiceInternal.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -50,6 +51,8 @@
 using ::android::base::WriteStringToFd;
 using ::std::string;
 
+using ISCSI = ::android::system::suspend::internal::ISuspendControlServiceInternal;
+
 namespace android {
 namespace system {
 namespace suspend {
@@ -425,7 +428,8 @@
     std::stringstream klStats;
     klStats << "Kernel wakesource stats: ";
     std::vector<WakeLockInfo> wlStats;
-    mStatsList.getWakeLockStats(&wlStats);
+    mStatsList.getWakeLockStats(
+        ISCSI::WAKE_LOCK_INFO_ACTIVE_COUNT | ISCSI::WAKE_LOCK_INFO_TOTAL_TIME, &wlStats);
 
     for (const WakeLockInfo& wake : wlStats) {
         if ((wake.isKernelWakelock) && (wake.activeCount > 0)) {
diff --git a/suspend/1.0/default/SystemSuspendUnitTest.cpp b/suspend/1.0/default/SystemSuspendUnitTest.cpp
index 03288ab..f6b6db3 100644
--- a/suspend/1.0/default/SystemSuspendUnitTest.cpp
+++ b/suspend/1.0/default/SystemSuspendUnitTest.cpp
@@ -30,6 +30,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <hidl/HidlTransportSupport.h>
+#include <suspend_service_flags.h>
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -228,7 +229,8 @@
 
     size_t getActiveWakeLockCount() {
         std::vector<WakeLockInfo> wlStats;
-        controlServiceInternal->getWakeLockStats(&wlStats);
+        controlServiceInternal->getWakeLockStatsFiltered(
+            ISuspendControlServiceInternal::WAKE_LOCK_INFO_ACTIVE_COUNT, &wlStats);
         return count_if(wlStats.begin(), wlStats.end(), [](auto entry) { return entry.isActive; });
     }
 
@@ -1199,9 +1201,10 @@
     /**
      * Returns wakelock stats.
      */
-    std::vector<WakeLockInfo> getWakelockStats() {
+    std::vector<WakeLockInfo> getWakelockStats(
+        int32_t selectBitmap = ISuspendControlServiceInternal::WAKE_LOCK_INFO_ALL_FIELDS) {
         std::vector<WakeLockInfo> wlStats;
-        controlServiceInternal->getWakeLockStats(&wlStats);
+        controlServiceInternal->getWakeLockStatsFiltered(selectBitmap, &wlStats);
         return wlStats;
     }
 
@@ -1265,6 +1268,24 @@
     };
 };
 
+class mock_flag_provider_interface : public suspend_service::flags::flag_provider_interface {
+   public:
+    MOCK_METHOD(bool, fast_kernel_wakelock_reporting, (), (override));
+};
+
+class ParameterizedSystemSuspendSameThreadTest : public SystemSuspendSameThreadTest,
+                                                 public ::testing::WithParamInterface<bool> {
+   protected:
+    void SetUp() override {
+        auto mock_flag_provider = std::make_unique<mock_flag_provider_interface>();
+        ON_CALL(*mock_flag_provider, fast_kernel_wakelock_reporting())
+            .WillByDefault(::testing::Return(GetParam()));
+        suspend_service::flags::provider_ = std::move(mock_flag_provider);
+
+        SystemSuspendSameThreadTest::SetUp();
+    }
+};
+
 // Test that getWakeLockStats has correct information about Native WakeLocks.
 TEST_F(SystemSuspendSameThreadTest, GetNativeWakeLockStats) {
     std::string fakeWlName = "FakeLock";
@@ -1311,8 +1332,11 @@
     ASSERT_EQ(nwlInfo.wakeupCount, 0);
 }
 
+INSTANTIATE_TEST_SUITE_P(ParameterizedSystemSuspendSameThreadTest,
+                         ParameterizedSystemSuspendSameThreadTest, ::testing::Bool());
+
 // Test that getWakeLockStats has correct information about Kernel WakeLocks.
-TEST_F(SystemSuspendSameThreadTest, GetKernelWakeLockStats) {
+TEST_P(ParameterizedSystemSuspendSameThreadTest, GetKernelWakeLockStats) {
     std::string fakeKwlName1 = "fakeKwl1";
     std::string fakeKwlName2 = "fakeKwl2";
     addKernelWakelock(fakeKwlName1);
@@ -1360,7 +1384,7 @@
 }
 
 // Test that getWakeLockStats has correct information about Native AND Kernel WakeLocks.
-TEST_F(SystemSuspendSameThreadTest, GetNativeAndKernelWakeLockStats) {
+TEST_P(ParameterizedSystemSuspendSameThreadTest, GetNativeAndKernelWakeLockStats) {
     std::string fakeNwlName = "fakeNwl";
     std::string fakeKwlName = "fakeKwl";
 
@@ -1825,6 +1849,42 @@
     ASSERT_EQ(wakeups[2].count, 2);
 }
 
+struct WakeLockInfoField {
+    int32_t bit = 0;
+    std::function<int(WakeLockInfo)> getter;
+    int64_t expectedValue;
+};
+
+// Test that selected fields are properly set.
+TEST_P(ParameterizedSystemSuspendSameThreadTest, GetKernelWakeLockStatsFiltered) {
+    using ISCSI = ISuspendControlServiceInternal;
+    static const WakeLockInfoField FIELDS[] = {
+        {ISCSI::WAKE_LOCK_INFO_ACTIVE_COUNT, [](WakeLockInfo wl) { return wl.activeCount; }, 1},
+        {ISCSI::WAKE_LOCK_INFO_LAST_CHANGE, [](WakeLockInfo wl) { return wl.lastChange; }, 2},
+        {ISCSI::WAKE_LOCK_INFO_MAX_TIME, [](WakeLockInfo wl) { return wl.maxTime; }, 3},
+        {ISCSI::WAKE_LOCK_INFO_TOTAL_TIME, [](WakeLockInfo wl) { return wl.totalTime; }, 4},
+        {ISCSI::WAKE_LOCK_INFO_ACTIVE_TIME, [](WakeLockInfo wl) { return wl.activeTime; }, 5},
+        {ISCSI::WAKE_LOCK_INFO_EVENT_COUNT, [](WakeLockInfo wl) { return wl.eventCount; }, 6},
+        {ISCSI::WAKE_LOCK_INFO_EXPIRE_COUNT, [](WakeLockInfo wl) { return wl.expireCount; }, 7},
+        {ISCSI::WAKE_LOCK_INFO_PREVENT_SUSPEND_TIME,
+         [](WakeLockInfo wl) { return wl.preventSuspendTime; }, 8},
+        {ISCSI::WAKE_LOCK_INFO_WAKEUP_COUNT, [](WakeLockInfo wl) { return wl.wakeupCount; }, 9},
+    };
+
+    std::string fakeKwlName1 = "fakeKwl1";
+    addKernelWakelock(fakeKwlName1, /* activeCount = */ 1, /* activeTime = */ 5,
+                      /* eventCount = */ 6,
+                      /* expireCount = */ 7, /* lastChange = */ 2, /* maxTime = */ 3,
+                      /* preventSuspendTime = */ 8, /* totalTime = */ 4, /* wakeupCount = */ 9);
+    for (auto field : FIELDS) {
+        std::vector<WakeLockInfo> infos = getWakelockStats(field.bit);
+        WakeLockInfo wli;
+        ASSERT_TRUE(findWakeLockInfoByName(infos, fakeKwlName1, &wli));
+        ASSERT_EQ(field.getter(wli), field.expectedValue)
+            << "Bit mask " << field.bit << " had unexpected value";
+    }
+}
+
 }  // namespace android
 
 int main(int argc, char** argv) {
diff --git a/suspend/1.0/default/WakeLockEntryList.cpp b/suspend/1.0/default/WakeLockEntryList.cpp
index 5a43501..f726586 100644
--- a/suspend/1.0/default/WakeLockEntryList.cpp
+++ b/suspend/1.0/default/WakeLockEntryList.cpp
@@ -20,6 +20,8 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android/system/suspend/internal/ISuspendControlServiceInternal.h>
+#include <suspend_service_flags.h>
 
 #include <iomanip>
 
@@ -27,12 +29,37 @@
 using android::base::ReadFdToString;
 using android::base::Readlink;
 using android::base::StringPrintf;
+using suspend_service::flags::fast_kernel_wakelock_reporting;
+
+using ISCSI = ::android::system::suspend::internal::ISuspendControlServiceInternal;
 
 namespace android {
 namespace system {
 namespace suspend {
 namespace V1_0 {
 
+namespace {
+
+struct BitAndFilename {
+    int32_t bit;
+    std::string filename;
+};
+
+const BitAndFilename FIELDS[] = {
+    {-1, "name"},
+    {ISCSI::WAKE_LOCK_INFO_ACTIVE_COUNT, "active_count"},
+    {ISCSI::WAKE_LOCK_INFO_LAST_CHANGE, "last_change_ms"},
+    {ISCSI::WAKE_LOCK_INFO_MAX_TIME, "max_time_ms"},
+    {ISCSI::WAKE_LOCK_INFO_TOTAL_TIME, "total_time_ms"},
+    {ISCSI::WAKE_LOCK_INFO_ACTIVE_TIME, "active_time_ms"},
+    {ISCSI::WAKE_LOCK_INFO_EVENT_COUNT, "event_count"},
+    {ISCSI::WAKE_LOCK_INFO_EXPIRE_COUNT, "expire_count"},
+    {ISCSI::WAKE_LOCK_INFO_PREVENT_SUSPEND_TIME, "prevent_suspend_time_ms"},
+    {ISCSI::WAKE_LOCK_INFO_WAKEUP_COUNT, "wakeup_count"},
+};
+
+}  // namespace
+
 static std::ostream& operator<<(std::ostream& out, const WakeLockInfo& entry) {
     const char* sep = " | ";
     const char* notApplicable = "---";
@@ -65,7 +92,7 @@
 
 std::ostream& operator<<(std::ostream& out, const WakeLockEntryList& list) {
     std::vector<WakeLockInfo> wlStats;
-    list.getWakeLockStats(&wlStats);
+    list.getWakeLockStats(ISCSI::WAKE_LOCK_INFO_ALL_FIELDS, &wlStats);
     int width = 194;
     const char* sep = " | ";
     std::stringstream ss;
@@ -324,20 +351,146 @@
     return info;
 }
 
-void WakeLockEntryList::getKernelWakelockStats(std::vector<WakeLockInfo>* aidl_return) const {
+/*
+ * Creates and returns a kernel wakelock entry with data read from mKernelWakelockStatsFd.
+ * Has been micro-optimized to reduce CPU time and wall time.
+ */
+WakeLockInfo WakeLockEntryList::createKernelEntry(ScratchSpace* ss, int wakeLockInfoFieldBitMask,
+                                                  const std::string& kwlId) const {
+    WakeLockInfo info;
+
+    info.activeCount = 0;
+    info.lastChange = 0;
+    info.maxTime = 0;
+    info.totalTime = 0;
+    info.isActive = false;
+    info.activeTime = 0;
+    info.isKernelWakelock = true;
+
+    info.pid = -1;  // N/A
+
+    info.eventCount = 0;
+    info.expireCount = 0;
+    info.preventSuspendTime = 0;
+    info.wakeupCount = 0;
+
+    for (const auto& field : FIELDS) {
+        const bool isNameField = field.bit == -1;
+        if (!isNameField && (wakeLockInfoFieldBitMask & field.bit) == 0) {
+            continue;
+        }
+
+        ss->statName = kwlId + "/" + field.filename;
+        int statFd = -1;
+
+        {
+            std::lock_guard<std::mutex> lock(mLock);
+            // Check if we have a valid cached file descriptor.
+            auto it = mFdCache.find(ss->statName);
+            if (it != mFdCache.end() && it->second >= 0) {
+                auto result = lseek(it->second, 0, SEEK_SET);
+                if (result < 0) {
+                    PLOG(ERROR) << "Could not seek to start of FD for " << ss->statName;
+                    mFdCache.erase(it);
+                    PLOG(ERROR) << "Closed the FD.";
+                } else {
+                    statFd = it->second;
+                }
+            }
+
+            if (statFd == -1) {
+                unique_fd tmpFd(TEMP_FAILURE_RETRY(
+                    openat(mKernelWakelockStatsFd, ss->statName.c_str(), O_CLOEXEC | O_RDONLY)));
+                if (tmpFd < 0) {
+                    PLOG(ERROR) << "Error opening " << ss->statName << " for " << kwlId;
+                    continue;
+                }
+                statFd = tmpFd;
+                mFdCache.insert(it, {ss->statName, std::move(tmpFd)});
+            }
+        }  // mLock is released here
+
+        ss->valStr.clear();
+        ssize_t n;
+        while ((n = TEMP_FAILURE_RETRY(read(statFd, &ss->readBuff[0], sizeof(ss->readBuff)))) > 0) {
+            ss->valStr.append(ss->readBuff, n);
+        }
+        if (n < 0) {
+            PLOG(ERROR) << "Error reading " << ss->statName;
+            {
+                std::lock_guard<std::mutex> lock(mLock);
+                mFdCache.erase(ss->statName);
+                PLOG(ERROR) << "Closed the FD.";
+            }
+            continue;
+        }
+
+        // Trim newline
+        ss->valStr.erase(std::remove(ss->valStr.begin(), ss->valStr.end(), '\n'), ss->valStr.end());
+
+        if (isNameField) {
+            info.name = ss->valStr;
+            continue;
+        }
+
+        int64_t statVal;
+        if (!ParseInt(ss->valStr, &statVal)) {
+            std::string path;
+            if (Readlink(StringPrintf("/proc/self/fd/%d", statFd), &path)) {
+                LOG(ERROR) << "Unexpected format for wakelock stat value (" << ss->valStr
+                           << ") from file: " << path;
+            } else {
+                LOG(ERROR) << "Unexpected format for wakelock stat value (" << ss->valStr << ")";
+            }
+            continue;
+        }
+
+        if (field.filename == "active_count") {
+            info.activeCount = statVal;
+        } else if (field.filename == "active_time_ms") {
+            info.activeTime = statVal;
+        } else if (field.filename == "event_count") {
+            info.eventCount = statVal;
+        } else if (field.filename == "expire_count") {
+            info.expireCount = statVal;
+        } else if (field.filename == "last_change_ms") {
+            info.lastChange = statVal;
+        } else if (field.filename == "max_time_ms") {
+            info.maxTime = statVal;
+        } else if (field.filename == "prevent_suspend_time_ms") {
+            info.preventSuspendTime = statVal;
+        } else if (field.filename == "total_time_ms") {
+            info.totalTime = statVal;
+        } else if (field.filename == "wakeup_count") {
+            info.wakeupCount = statVal;
+        }
+    }
+
+    // Derived stats
+    info.isActive = info.activeTime > 0;
+
+    return info;
+}
+
+void WakeLockEntryList::getKernelWakelockStats(int wakeLockInfoFieldBitMask,
+                                               std::vector<WakeLockInfo>* aidl_return) const {
     std::unique_ptr<DIR, decltype(&closedir)> dp(fdopendir(dup(mKernelWakelockStatsFd.get())),
                                                  &closedir);
     if (dp) {
         // rewinddir, else subsequent calls will not get any kernel wakelocks.
         rewinddir(dp.get());
 
+        ScratchSpace ss;
         struct dirent* de;
         while ((de = readdir(dp.get()))) {
             std::string kwlId(de->d_name);
             if ((kwlId == ".") || (kwlId == "..")) {
                 continue;
             }
-            WakeLockInfo entry = createKernelEntry(kwlId);
+            WakeLockInfo entry = fast_kernel_wakelock_reporting()
+                                     ? createKernelEntry(&ss, wakeLockInfoFieldBitMask, kwlId)
+                                     : createKernelEntry(kwlId);
+
             aidl_return->emplace_back(std::move(entry));
         }
     }
@@ -346,7 +499,7 @@
 void WakeLockEntryList::updateOnAcquire(const std::string& name, int pid) {
     TimestampType timeNow = getTimeNow();
 
-    std::lock_guard<std::mutex> lock(mStatsLock);
+    std::lock_guard<std::mutex> lock(mLock);
 
     auto key = std::make_pair(name, pid);
     auto it = mLookupTable.find(key);
@@ -372,7 +525,7 @@
 void WakeLockEntryList::updateOnRelease(const std::string& name, int pid) {
     TimestampType timeNow = getTimeNow();
 
-    std::lock_guard<std::mutex> lock(mStatsLock);
+    std::lock_guard<std::mutex> lock(mLock);
 
     auto key = std::make_pair(name, pid);
     auto it = mLookupTable.find(key);
@@ -406,7 +559,7 @@
  * Updates the native wakelock stats based on the current time.
  */
 void WakeLockEntryList::updateNow() {
-    std::lock_guard<std::mutex> lock(mStatsLock);
+    std::lock_guard<std::mutex> lock(mLock);
 
     TimestampType timeNow = getTimeNow();
 
@@ -421,15 +574,16 @@
     }
 }
 
-void WakeLockEntryList::getWakeLockStats(std::vector<WakeLockInfo>* aidl_return) const {
+void WakeLockEntryList::getWakeLockStats(int wakeLockInfoFieldBitMask,
+                                         std::vector<WakeLockInfo>* aidl_return) const {
     // Under no circumstances should the lock be held while getting kernel wakelock stats
     {
-        std::lock_guard<std::mutex> lock(mStatsLock);
+        std::lock_guard<std::mutex> lock(mLock);
         for (const WakeLockInfo& entry : mStats) {
             aidl_return->emplace_back(entry);
         }
     }
-    getKernelWakelockStats(aidl_return);
+    getKernelWakelockStats(wakeLockInfoFieldBitMask, aidl_return);
 }
 
 }  // namespace V1_0
diff --git a/suspend/1.0/default/WakeLockEntryList.h b/suspend/1.0/default/WakeLockEntryList.h
index 1ebc411..d81727a 100644
--- a/suspend/1.0/default/WakeLockEntryList.h
+++ b/suspend/1.0/default/WakeLockEntryList.h
@@ -49,16 +49,32 @@
     // updateNow() should be called before getWakeLockStats() to ensure stats are
     // updated wrt the current time.
     void updateNow();
-    void getWakeLockStats(std::vector<WakeLockInfo>* aidl_return) const;
+    void getWakeLockStats(int wakeLockInfoFieldBitMask,
+                          std::vector<WakeLockInfo>* aidl_return) const;
     friend std::ostream& operator<<(std::ostream& out, const WakeLockEntryList& list);
 
    private:
-    void evictIfFull() REQUIRES(mStatsLock);
-    void insertEntry(WakeLockInfo entry) REQUIRES(mStatsLock);
-    void deleteEntry(std::list<WakeLockInfo>::iterator entry) REQUIRES(mStatsLock);
+    void evictIfFull() REQUIRES(mLock);
+    void insertEntry(WakeLockInfo entry) REQUIRES(mLock);
+    void deleteEntry(std::list<WakeLockInfo>::iterator entry) REQUIRES(mLock);
     WakeLockInfo createNativeEntry(const std::string& name, int pid, TimestampType timeNow) const;
     WakeLockInfo createKernelEntry(const std::string& name) const;
-    void getKernelWakelockStats(std::vector<WakeLockInfo>* aidl_return) const;
+
+    // Used by createKernelEntry to reduce heap churn on successive calls.
+    struct ScratchSpace {
+        static constexpr const int BUFF_SIZE = 1024;
+        char readBuff[BUFF_SIZE];
+        std::string statName, valStr;
+        ScratchSpace() {
+            valStr.reserve(BUFF_SIZE);
+            statName.reserve(BUFF_SIZE);
+        }
+    };
+    WakeLockInfo createKernelEntry(ScratchSpace* ss, int wakeLockInfoFieldBitMask,
+                                   const std::string& name) const;
+
+    void getKernelWakelockStats(int wakeLockInfoFieldBitMask,
+                                std::vector<WakeLockInfo>* aidl_return) const;
 
     // Hash for WakeLockEntry key (pair<std::string, int>)
     struct LockHash {
@@ -67,17 +83,18 @@
         }
     };
 
+    mutable std::mutex mLock;
+
     size_t mCapacity;
     unique_fd mKernelWakelockStatsFd;
-
-    mutable std::mutex mStatsLock;
+    mutable std::unordered_map<std::string, unique_fd> mFdCache GUARDED_BY(mLock);
 
     // std::list and std::unordered map are used to support both inserting a stat
     // and eviction of the LRU stat in O(1) time. The LRU stat is maintained at
     // the back of the list.
-    std::list<WakeLockInfo> mStats GUARDED_BY(mStatsLock);
+    std::list<WakeLockInfo> mStats GUARDED_BY(mLock);
     std::unordered_map<std::pair<std::string, int>, std::list<WakeLockInfo>::iterator, LockHash>
-        mLookupTable GUARDED_BY(mStatsLock);
+        mLookupTable GUARDED_BY(mLock);
 };
 
 }  // namespace V1_0
diff --git a/suspend/1.0/default/flags.aconfig b/suspend/1.0/default/flags.aconfig
new file mode 100644
index 0000000..c943950
--- /dev/null
+++ b/suspend/1.0/default/flags.aconfig
@@ -0,0 +1,9 @@
+package: "suspend_service.flags"
+container: "system"
+
+flag {
+  name: "fast_kernel_wakelock_reporting"
+  namespace: "android_kernel"
+  description: "Controls using new codepath to speed up polling of /sys/class/wakeup for kernel wakelocks."
+  bug: "364368163"
+}
\ No newline at end of file
diff --git a/suspend/aidl/android/system/suspend/internal/ISuspendControlServiceInternal.aidl b/suspend/aidl/android/system/suspend/internal/ISuspendControlServiceInternal.aidl
index 8e0a9a2..065d486 100644
--- a/suspend/aidl/android/system/suspend/internal/ISuspendControlServiceInternal.aidl
+++ b/suspend/aidl/android/system/suspend/internal/ISuspendControlServiceInternal.aidl
@@ -46,6 +46,12 @@
     WakeLockInfo[] getWakeLockStats();
 
     /**
+     * Returns a list of wake lock stats. Fields not selected with the
+     * bit mask are in an undefined state (see WAKE_LOCK_INFO_* below).
+     */
+    WakeLockInfo[] getWakeLockStatsFiltered(int wakeLockInfoFieldBitMask);
+
+    /**
      * Returns a list of wakeup stats.
      */
     WakeupInfo[] getWakeupStats();
@@ -54,4 +60,27 @@
      * Returns stats related to suspend.
      */
     SuspendInfo getSuspendStats();
+
+    /**
+     * Used to select fields from WakeLockInfo that getWakeLockStats should return.
+     * This is in addition to the name of the wake lock, which is always returned.
+     */
+    const int WAKE_LOCK_INFO_ACTIVE_COUNT = 1 << 0;
+    const int WAKE_LOCK_INFO_LAST_CHANGE = 1 << 1;
+    const int WAKE_LOCK_INFO_MAX_TIME = 1 << 2;
+    const int WAKE_LOCK_INFO_TOTAL_TIME = 1 << 3;
+    const int WAKE_LOCK_INFO_IS_ACTIVE = 1 << 4;
+    const int WAKE_LOCK_INFO_ACTIVE_TIME = 1 << 5;
+    const int WAKE_LOCK_INFO_IS_KERNEL_WAKELOCK = 1 << 6;
+
+    // Specific to Native wake locks.
+    const int WAKE_LOCK_INFO_PID = 1 << 7;
+
+    // Specific to Kernel wake locks.
+    const int WAKE_LOCK_INFO_EVENT_COUNT = 1 << 8;
+    const int WAKE_LOCK_INFO_EXPIRE_COUNT = 1 << 9;
+    const int WAKE_LOCK_INFO_PREVENT_SUSPEND_TIME = 1 << 10;
+    const int WAKE_LOCK_INFO_WAKEUP_COUNT = 1 << 11;
+
+    const int WAKE_LOCK_INFO_ALL_FIELDS = (1 << 12) - 1;
 }