xref: /aosp_15_r20/frameworks/native/libs/battery/MultiStateCounter.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  * Android BPF library - public API
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #pragma once
19 
20 #include <inttypes.h>
21 #include <log/log.h>
22 #include <time.h>
23 #include <sstream>
24 #include <string>
25 
26 /**
27  * An object that can track changes of some value over time, taking into account an additional
28  * dimension: the object's state.  As the tracked value changes, the deltas are distributed
29  * among the object states in accordance with the time spent in those states.
30  */
31 namespace android {
32 namespace battery {
33 
34 #define REPORTED_INVALID_TIMESTAMP_DELTA_MS 60000
35 
36 typedef uint16_t state_t;
37 
38 template <class T, class V>
39 class MultiStateCounter {
40     const uint16_t stateCount;
41     const V emptyValue;
42     state_t currentState;
43     time_t lastStateChangeTimestamp;
44     T lastValue;
45     time_t lastUpdateTimestamp;
46     T deltaValue;
47     bool isEnabled;
48 
49     struct State {
50         time_t timeInStateSinceUpdate;
51         T counter;
52     };
53 
54     State* states;
55 
56 public:
57     MultiStateCounter(uint16_t stateCount, const V& emptyValue);
58 
59     virtual ~MultiStateCounter();
60 
61     void setEnabled(bool enabled, time_t timestamp);
62 
63     void setState(state_t state, time_t timestamp);
64 
65     /**
66      * Copies the current state and accumulated times-in-state from the source. Resets
67      * the accumulated value.
68      */
69     void copyStatesFrom(const MultiStateCounter<T, V> &source);
70 
71     void setValue(state_t state, const V& value);
72 
73     /**
74      * Updates the value by distributing the delta from the previously set value
75      * among states according to their respective time-in-state.
76      * Returns the delta from the previously set value.
77      */
78     const V& updateValue(const V& value, time_t timestamp);
79 
80     /**
81      * Updates the value by distributing the specified increment among states according
82      * to their respective time-in-state.
83      */
84     void incrementValue(const V& increment, time_t timestamp);
85 
86     /**
87      * Adds the specified increment to the value for the current state, without affecting
88      * the last updated value or timestamp.  Ignores partial time-in-state: the entirety of
89      * the increment is given to the current state.
90      */
91     void addValue(const V& increment);
92 
93     void reset();
94 
95     uint16_t getStateCount();
96 
97     const V& getCount(state_t state);
98 
99     std::string toString();
100 
101 private:
102     /**
103      * Subtracts previousValue from newValue and returns the result in outValue.
104      * Returns true iff the combination of previousValue and newValue is valid
105      * (newValue >= prevValue)
106      */
107     bool delta(const T& previousValue, const V& newValue, T* outValue) const;
108 
109     /**
110      * Adds value2 to value1 and stores the result in value1.  Denominator is
111      * guaranteed to be non-zero.
112      */
113     void add(T* value1, const V& value2, const uint64_t numerator,
114              const uint64_t denominator) const;
115 };
116 
117 // ---------------------- MultiStateCounter Implementation -------------------------
118 // Since MultiStateCounter is a template, the implementation must be inlined.
119 
120 template <class T, class V>
MultiStateCounter(uint16_t stateCount,const V & emptyValue)121 MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue)
122       : stateCount(stateCount),
123         emptyValue(emptyValue),
124         currentState(0),
125         lastStateChangeTimestamp(-1),
126         lastValue(emptyValue),
127         lastUpdateTimestamp(-1),
128         deltaValue(emptyValue),
129         isEnabled(true) {
130     states = new State[stateCount];
131     for (int i = 0; i < stateCount; i++) {
132         states[i].timeInStateSinceUpdate = 0;
133         states[i].counter = emptyValue;
134     }
135 }
136 
137 template <class T, class V>
~MultiStateCounter()138 MultiStateCounter<T, V>::~MultiStateCounter() {
139     delete[] states;
140 };
141 
142 template <class T, class V>
setEnabled(bool enabled,time_t timestamp)143 void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) {
144     if (enabled == isEnabled) {
145         return;
146     }
147 
148     if (isEnabled) {
149         // Confirm the current state for the side-effect of updating the time-in-state
150         // counter for the current state.
151         setState(currentState, timestamp);
152         isEnabled = false;
153     } else {
154         // If the counter is being enabled with an out-of-order timestamp, just push back
155         // the timestamp to avoid having the situation where
156         // timeInStateSinceUpdate > timeSinceUpdate
157         if (timestamp < lastUpdateTimestamp) {
158             timestamp = lastUpdateTimestamp;
159         }
160 
161         if (lastStateChangeTimestamp >= 0) {
162             lastStateChangeTimestamp = timestamp;
163         }
164         isEnabled = true;
165     }
166 }
167 
168 template <class T, class V>
setState(state_t state,time_t timestamp)169 void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) {
170     if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
171         // If the update arrived out-of-order, just push back the timestamp to
172         // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
173         if (timestamp < lastUpdateTimestamp) {
174             timestamp = lastUpdateTimestamp;
175         }
176 
177         if (timestamp >= lastStateChangeTimestamp) {
178             states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
179         } else {
180             if (timestamp < lastStateChangeTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
181                 ALOGE("setState is called with an earlier timestamp: %lu, "
182                       "previous timestamp: %lu\n",
183                       (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
184             }
185 
186             // The accumulated durations have become unreliable. For example, if the timestamp
187             // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
188             // we would get 4000, which is greater than (last - first). This could lead to
189             // counts exceeding 100%.
190             for (int i = 0; i < stateCount; i++) {
191                 states[i].timeInStateSinceUpdate = 0;
192             }
193         }
194     }
195     currentState = state;
196     lastStateChangeTimestamp = timestamp;
197 }
198 
199 template <class T, class V>
copyStatesFrom(const MultiStateCounter<T,V> & source)200 void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) {
201     if (stateCount != source.stateCount) {
202         ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
203         return;
204     }
205 
206     currentState = source.currentState;
207     for (int i = 0; i < stateCount; i++) {
208         states[i].timeInStateSinceUpdate = source.states[i].timeInStateSinceUpdate;
209         states[i].counter = emptyValue;
210     }
211     lastStateChangeTimestamp = source.lastStateChangeTimestamp;
212     lastUpdateTimestamp = source.lastUpdateTimestamp;
213 }
214 
215 template <class T, class V>
setValue(state_t state,const V & value)216 void MultiStateCounter<T, V>::setValue(state_t state, const V& value) {
217     states[state].counter = value;
218 }
219 
220 template <class T, class V>
updateValue(const V & value,time_t timestamp)221 const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) {
222     const V* returnValue = &emptyValue;
223 
224     // If the counter is disabled, we ignore the update, except when the counter got disabled after
225     // the previous update, in which case we still need to pick up the residual delta.
226     if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
227         // If the update arrived out of order, just push back the timestamp to
228         // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
229         if (timestamp < lastStateChangeTimestamp) {
230             timestamp = lastStateChangeTimestamp;
231         }
232 
233         // Confirm the current state for the side-effect of updating the time-in-state
234         // counter for the current state.
235         setState(currentState, timestamp);
236 
237         if (lastUpdateTimestamp >= 0) {
238             if (timestamp > lastUpdateTimestamp) {
239                 if (delta(lastValue, value, &deltaValue)) {
240                     returnValue = &deltaValue;
241                     time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
242                     for (int i = 0; i < stateCount; i++) {
243                         time_t timeInState = states[i].timeInStateSinceUpdate;
244                         if (timeInState) {
245                             add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
246                             states[i].timeInStateSinceUpdate = 0;
247                         }
248                     }
249                 } else {
250                     std::stringstream str;
251                     str << "updateValue is called with a value " << value
252                         << ", which is lower than the previous value " << lastValue
253                         << "\n";
254                     ALOGE("%s", str.str().c_str());
255 
256                     for (int i = 0; i < stateCount; i++) {
257                         states[i].timeInStateSinceUpdate = 0;
258                     }
259                 }
260             } else if (timestamp < lastUpdateTimestamp) {
261                 if (timestamp < lastUpdateTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
262                     ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
263                           (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
264                 }
265 
266                 for (int i = 0; i < stateCount; i++) {
267                     states[i].timeInStateSinceUpdate = 0;
268                 }
269             }
270         }
271     }
272     lastValue = value;
273     lastUpdateTimestamp = timestamp;
274     return *returnValue;
275 }
276 
277 template <class T, class V>
incrementValue(const V & increment,time_t timestamp)278 void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) {
279 //    T newValue;
280 //    newValue = lastValue; // Copy assignment, not initialization.
281     T newValue = lastValue;
282     add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
283     updateValue(newValue, timestamp);
284 }
285 
286 template <class T, class V>
addValue(const V & value)287 void MultiStateCounter<T, V>::addValue(const V& value) {
288     if (!isEnabled) {
289         return;
290     }
291     add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
292 }
293 
294 template <class T, class V>
reset()295 void MultiStateCounter<T, V>::reset() {
296     lastStateChangeTimestamp = -1;
297     lastUpdateTimestamp = -1;
298     for (int i = 0; i < stateCount; i++) {
299         states[i].timeInStateSinceUpdate = 0;
300         states[i].counter = emptyValue;
301     }
302 }
303 
304 template <class T, class V>
getStateCount()305 uint16_t MultiStateCounter<T, V>::getStateCount() {
306     return stateCount;
307 }
308 
309 template <class T, class V>
getCount(state_t state)310 const V& MultiStateCounter<T, V>::getCount(state_t state) {
311     return states[state].counter;
312 }
313 
314 template <class T, class V>
toString()315 std::string MultiStateCounter<T, V>::toString() {
316     std::stringstream str;
317 //    str << "LAST VALUE: " << valueToString(lastValue);
318     str << "[";
319     for (int i = 0; i < stateCount; i++) {
320         if (i != 0) {
321             str << ", ";
322         }
323         str << i << ": " << states[i].counter;
324         if (states[i].timeInStateSinceUpdate > 0) {
325             str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
326         }
327     }
328     str << "]";
329     if (lastUpdateTimestamp >= 0) {
330         str << " updated: " << lastUpdateTimestamp;
331     }
332     if (lastStateChangeTimestamp >= 0) {
333         str << " currentState: " << currentState;
334         if (lastStateChangeTimestamp > lastUpdateTimestamp) {
335             str << " stateChanged: " << lastStateChangeTimestamp;
336         }
337     } else {
338         str << " currentState: none";
339     }
340     if (!isEnabled) {
341         str << " disabled";
342     }
343     return str.str();
344 }
345 
346 } // namespace battery
347 } // namespace android
348