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