• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
registerListener(const wp<StateListener> & listener)67 void StateTracker::registerListener(const wp<StateListener>& listener) {
68     mListeners.insert(listener);
69 }
70 
unregisterListener(const wp<StateListener> & listener)71 void StateTracker::unregisterListener(const wp<StateListener>& listener) {
72     mListeners.erase(listener);
73 }
74 
getStateValue(const HashableDimensionKey & queryKey,FieldValue * output) const75 bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
76     output->mField = mField;
77 
78     if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
79         output->mValue = it->second.state;
80         return true;
81     }
82 
83     // Set the state value to kStateUnknown if query key is not found in state map.
84     output->mValue = kStateUnknown;
85     VLOG("StateTracker did not find state value for query key %s", queryKey.toString().c_str());
86     return false;
87 }
88 
handleReset(const int64_t eventTimeNs,const FieldValue & newState)89 void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) {
90     VLOG("StateTracker handle reset");
91     for (auto& [primaryKey, stateValueInfo] : mStateMap) {
92         updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
93                                  false /* nested; treat this state change as not nested */,
94                                  stateValueInfo);
95     }
96 }
97 
clearStateForPrimaryKey(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey)98 void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
99                                            const HashableDimensionKey& primaryKey) {
100     VLOG("StateTracker clear state for primary key");
101     const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
102             mStateMap.find(primaryKey);
103 
104     // If there is no entry for the primaryKey in mStateMap, then the state is already
105     // kStateUnknown.
106     const FieldValue state(mField, Value(kStateUnknown));
107     if (it != mStateMap.end()) {
108         updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
109                                  false /* nested; treat this state change as not nested */,
110                                  it->second);
111     }
112 }
113 
updateStateForPrimaryKey(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey,const FieldValue & newState,const bool nested,StateValueInfo & stateValueInfo)114 void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
115                                             const HashableDimensionKey& primaryKey,
116                                             const FieldValue& newState, const bool nested,
117                                             StateValueInfo& stateValueInfo) {
118     FieldValue oldState;
119     oldState.mField = mField;
120     oldState.mValue.setInt(stateValueInfo.state);
121     const int32_t oldStateValue = stateValueInfo.state;
122     const int32_t newStateValue = newState.mValue.int_value;
123 
124     // Update state map and notify listeners if state has changed.
125     // Every state event triggers a state overwrite.
126     if (!nested) {
127         if (newStateValue != oldStateValue) {
128             stateValueInfo.state = newStateValue;
129             stateValueInfo.count = 1;
130             notifyListeners(eventTimeNs, primaryKey, oldState, newState);
131         }
132 
133     // Update state map for nested counting case.
134     //
135     // Nested counting is only allowed for binary state events such as ON/OFF or
136     // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
137     // events: ON, ON, OFF. The state will still be ON until we see the same
138     // number of OFF events as ON events.
139     //
140     // In atoms.proto, a state atom with nested counting enabled
141     // must only have 2 states. There is no enforcemnt here of this requirement.
142     // The atom must be logged correctly.
143     } else if (newStateValue == kStateUnknown) {
144         if (oldStateValue != kStateUnknown) {
145             notifyListeners(eventTimeNs, primaryKey, oldState, newState);
146         }
147     } else if (oldStateValue == kStateUnknown) {
148         stateValueInfo.state = newStateValue;
149         stateValueInfo.count = 1;
150         notifyListeners(eventTimeNs, primaryKey, oldState, newState);
151     } else if (oldStateValue == newStateValue) {
152         stateValueInfo.count++;
153     } else if (--stateValueInfo.count == 0) {
154         stateValueInfo.state = newStateValue;
155         stateValueInfo.count = 1;
156         notifyListeners(eventTimeNs, primaryKey, oldState, newState);
157     }
158 
159     // Clear primary key entry from state map if state is now unknown.
160     // stateValueInfo points to a value in mStateMap and should not be accessed after erasing the
161     // entry
162     if (newStateValue == kStateUnknown) {
163         mStateMap.erase(primaryKey);
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 (const 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         return false;
182     }
183 
184     *output = event.getValues()[exclusiveStateFieldIndex.value()];
185     return true;
186 }
187 
188 }  // namespace statsd
189 }  // namespace os
190 }  // namespace android
191