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