1 /*
2  * Copyright (C) 2020 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 <dataproviders/DisplayStateResidencyDataProvider.h>
18 
19 #include <android-base/chrono_utils.h>
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 
23 #include <chrono>
24 #include <cstdio>
25 #include <cstring>
26 
27 namespace aidl {
28 namespace android {
29 namespace hardware {
30 namespace power {
31 namespace stats {
32 
DisplayStateResidencyDataProvider(std::string name,std::string path,std::vector<std::string> states)33 DisplayStateResidencyDataProvider::DisplayStateResidencyDataProvider(
34         std::string name, std::string path, std::vector<std::string> states)
35     : mPath(std::move(path)),
36       mName(std::move(name)),
37       mStates(states),
38       mCurState(-1),
39       mLooper(new ::android::Looper(true)) {
40     // Construct mResidencies
41     mResidencies.reserve(mStates.size());
42     for (int32_t i = 0; i < mStates.size(); ++i) {
43         StateResidency p = {.id = i};
44         mResidencies.emplace_back(p);
45     }
46 
47     // Open display state file descriptor
48     LOG(VERBOSE) << "Opening " << mPath;
49     mFd = open(mPath.c_str(), O_RDONLY | O_NONBLOCK);
50     if (mFd < 0) {
51         PLOG(ERROR) << ":Failed to open file " << mPath;
52         return;
53     }
54 
55     // Add display state file descriptor to be polled by the looper
56     mLooper->addFd(mFd, 0, ::android::Looper::EVENT_ERROR, nullptr, nullptr);
57 
58     // Run the thread that will poll for changes to display state
59     LOG(VERBOSE) << "Starting DisplayStateWatcherThread";
60     mThread = std::thread(&DisplayStateResidencyDataProvider::pollLoop, this);
61 }
62 
~DisplayStateResidencyDataProvider()63 DisplayStateResidencyDataProvider::~DisplayStateResidencyDataProvider() {
64     if (mFd >= 0) {
65         close(mFd);
66     }
67 }
68 
getStateResidencies(std::unordered_map<std::string,std::vector<StateResidency>> * residencies)69 bool DisplayStateResidencyDataProvider::getStateResidencies(
70         std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
71     std::scoped_lock lk(mLock);
72 
73     // Get current time since boot in milliseconds
74     uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
75                            ::android::base::boot_clock::now().time_since_epoch())
76                            .count();
77 
78     // Construct residency result based on current residency data
79     auto result = mResidencies;
80 
81     if (mCurState > -1) {
82         result[mCurState].totalTimeInStateMs += now - result[mCurState].lastEntryTimestampMs;
83     }
84 
85     residencies->emplace(mName, result);
86     return true;
87 }
88 
getInfo()89 std::unordered_map<std::string, std::vector<State>> DisplayStateResidencyDataProvider::getInfo() {
90     std::vector<State> stateInfos;
91     stateInfos.reserve(mStates.size());
92     for (int32_t i = 0; i < mStates.size(); ++i) {
93         stateInfos.push_back({.id = i, .name = mStates[i]});
94     }
95 
96     return {{mName, stateInfos}};
97 }
98 
99 // Called when there is new data to be read from
100 // display state file descriptor indicating a state change
updateStats()101 void DisplayStateResidencyDataProvider::updateStats() {
102     char data[32];
103 
104     // Get current time since boot in milliseconds
105     uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
106                            ::android::base::boot_clock::now().time_since_epoch())
107                            .count();
108     // Read display state
109     ssize_t ret = pread(mFd, data, sizeof(data) - 1, 0);
110     if (ret < 0) {
111         PLOG(ERROR) << "Failed to read display state";
112         return;
113     }
114     data[ret] = '\0';
115 
116     LOG(VERBOSE) << "display state: " << data;
117 
118     // Update residency stats based on state read
119     {  // acquire lock
120         std::scoped_lock lk(mLock);
121         for (uint32_t i = 0; i < mStates.size(); ++i) {
122             if (strstr(data, mStates[i].c_str())) {
123                 // Update total time of the previous state
124                 if (mCurState > -1) {
125                     mResidencies[mCurState].totalTimeInStateMs +=
126                             now - mResidencies[mCurState].lastEntryTimestampMs;
127                 }
128 
129                 // Set current state
130                 mCurState = i;
131                 mResidencies[i].totalStateEntryCount++;
132                 mResidencies[i].lastEntryTimestampMs = now;
133                 break;
134             }
135         }
136     }  // release lock
137 }
138 
pollLoop()139 void DisplayStateResidencyDataProvider::pollLoop() {
140     LOG(VERBOSE) << "DisplayStateResidencyDataProvider polling...";
141     while (true) {
142         // Poll for display state changes. Timeout set to poll indefinitely
143         if (mLooper->pollOnce(-1) >= 0) {
144             updateStats();
145         }
146     }
147 }
148 
149 }  // namespace stats
150 }  // namespace power
151 }  // namespace hardware
152 }  // namespace android
153 }  // namespace aidl
154