1 /*
2 * Copyright (C) 2019, 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 #define STATSD_DEBUG false // STOPSHIP if true
18 #include "Log.h"
19
20 #include "stats_util.h"
21
22 #include "StateTracker.h"
23
24 namespace android {
25 namespace os {
26 namespace statsd {
27
StateTracker(const int32_t atomId)28 StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
29 }
30
onLogEvent(const LogEvent & event)31 void StateTracker::onLogEvent(const LogEvent& event) {
32 const int64_t eventTimeNs = event.GetElapsedTimestampNs();
33
34 // Parse event for primary field values i.e. primary key.
35 HashableDimensionKey primaryKey;
36 filterPrimaryKey(event.getValues(), &primaryKey);
37
38 FieldValue newState;
39 if (!getStateFieldValueFromLogEvent(event, &newState)) {
40 ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
41 clearStateForPrimaryKey(eventTimeNs, primaryKey);
42 return;
43 }
44
45 mField.setField(newState.mField.getField());
46
47 if (newState.mValue.getType() != INT) {
48 ALOGE("StateTracker error extracting state from log event. Type: %d",
49 newState.mValue.getType());
50 clearStateForPrimaryKey(eventTimeNs, primaryKey);
51 return;
52 }
53
54 if (int resetState = event.getResetState(); resetState != -1) {
55 VLOG("StateTracker new reset state: %d", resetState);
56 const FieldValue resetStateFieldValue(mField, Value(resetState));
57 handleReset(eventTimeNs, resetStateFieldValue);
58 return;
59 }
60
61 const bool nested = newState.mAnnotations.isNested();
62 StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
63 updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
64 }
65
registerListener(wp<StateListener> listener)66 void StateTracker::registerListener(wp<StateListener> listener) {
67 mListeners.insert(listener);
68 }
69
unregisterListener(wp<StateListener> listener)70 void StateTracker::unregisterListener(wp<StateListener> listener) {
71 mListeners.erase(listener);
72 }
73
getStateValue(const HashableDimensionKey & queryKey,FieldValue * output) const74 bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
75 output->mField = mField;
76
77 if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
78 output->mValue = it->second.state;
79 return true;
80 }
81
82 // Set the state value to kStateUnknown if query key is not found in state map.
83 output->mValue = kStateUnknown;
84 VLOG("StateTracker did not find state value for query key %s", queryKey.toString().c_str());
85 return false;
86 }
87
handleReset(const int64_t eventTimeNs,const FieldValue & newState)88 void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) {
89 VLOG("StateTracker handle reset");
90 for (auto& [primaryKey, stateValueInfo] : mStateMap) {
91 updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
92 false /* nested; treat this state change as not nested */,
93 &stateValueInfo);
94 }
95 }
96
clearStateForPrimaryKey(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey)97 void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
98 const HashableDimensionKey& primaryKey) {
99 VLOG("StateTracker clear state for primary key");
100 const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
101 mStateMap.find(primaryKey);
102
103 // If there is no entry for the primaryKey in mStateMap, then the state is already
104 // kStateUnknown.
105 const FieldValue state(mField, Value(kStateUnknown));
106 if (it != mStateMap.end()) {
107 updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
108 false /* nested; treat this state change as not nested */,
109 &it->second);
110 }
111 }
112
updateStateForPrimaryKey(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey,const FieldValue & newState,const bool nested,StateValueInfo * stateValueInfo)113 void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
114 const HashableDimensionKey& primaryKey,
115 const FieldValue& newState, const bool nested,
116 StateValueInfo* stateValueInfo) {
117 FieldValue oldState;
118 oldState.mField = mField;
119 oldState.mValue.setInt(stateValueInfo->state);
120 const int32_t oldStateValue = stateValueInfo->state;
121 const int32_t newStateValue = newState.mValue.int_value;
122
123 if (kStateUnknown == newStateValue) {
124 mStateMap.erase(primaryKey);
125 }
126
127 // Update state map for non-nested counting case.
128 // Every state event triggers a state overwrite.
129 if (!nested) {
130 stateValueInfo->state = newStateValue;
131 stateValueInfo->count = 1;
132
133 // Notify listeners if state has changed.
134 if (oldStateValue != newStateValue) {
135 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
136 }
137 return;
138 }
139
140 // Update state map for nested counting case.
141 //
142 // Nested counting is only allowed for binary state events such as ON/OFF or
143 // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
144 // events: ON, ON, OFF. The state will still be ON until we see the same
145 // number of OFF events as ON events.
146 //
147 // In atoms.proto, a state atom with nested counting enabled
148 // must only have 2 states. There is no enforcemnt here of this requirement.
149 // The atom must be logged correctly.
150 if (kStateUnknown == newStateValue) {
151 if (kStateUnknown != oldStateValue) {
152 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
153 }
154 } else if (oldStateValue == kStateUnknown) {
155 stateValueInfo->state = newStateValue;
156 stateValueInfo->count = 1;
157 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
158 } else if (oldStateValue == newStateValue) {
159 stateValueInfo->count++;
160 } else if (--stateValueInfo->count == 0) {
161 stateValueInfo->state = newStateValue;
162 stateValueInfo->count = 1;
163 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
164 }
165 }
166
notifyListeners(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey,const FieldValue & oldState,const FieldValue & newState)167 void StateTracker::notifyListeners(const int64_t eventTimeNs,
168 const HashableDimensionKey& primaryKey,
169 const FieldValue& oldState, const FieldValue& newState) {
170 for (auto l : mListeners) {
171 auto sl = l.promote();
172 if (sl != nullptr) {
173 sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
174 }
175 }
176 }
177
getStateFieldValueFromLogEvent(const LogEvent & event,FieldValue * output)178 bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
179 const std::optional<size_t>& exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
180 if (!exclusiveStateFieldIndex) {
181 ALOGE("error extracting state from log event. Missing exclusive state field.");
182 return false;
183 }
184
185 *output = event.getValues()[exclusiveStateFieldIndex.value()];
186 return true;
187 }
188
189 } // namespace statsd
190 } // namespace os
191 } // namespace android
192