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