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