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 DEBUG true  // 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     return false;
85 }
86 
handleReset(const int64_t eventTimeNs,const FieldValue & newState)87 void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) {
88     VLOG("StateTracker handle reset");
89     for (auto& [primaryKey, stateValueInfo] : mStateMap) {
90         updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
91                                  false /* nested; treat this state change as not nested */,
92                                  &stateValueInfo);
93     }
94 }
95 
clearStateForPrimaryKey(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey)96 void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
97                                            const HashableDimensionKey& primaryKey) {
98     VLOG("StateTracker clear state for primary key");
99     const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
100             mStateMap.find(primaryKey);
101 
102     // If there is no entry for the primaryKey in mStateMap, then the state is already
103     // kStateUnknown.
104     const FieldValue state(mField, Value(kStateUnknown));
105     if (it != mStateMap.end()) {
106         updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
107                                  false /* nested; treat this state change as not nested */,
108                                  &it->second);
109     }
110 }
111 
updateStateForPrimaryKey(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey,const FieldValue & newState,const bool nested,StateValueInfo * stateValueInfo)112 void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
113                                             const HashableDimensionKey& primaryKey,
114                                             const FieldValue& newState, const bool nested,
115                                             StateValueInfo* stateValueInfo) {
116     FieldValue oldState;
117     oldState.mField = mField;
118     oldState.mValue.setInt(stateValueInfo->state);
119     const int32_t oldStateValue = stateValueInfo->state;
120     const int32_t newStateValue = newState.mValue.int_value;
121 
122     if (kStateUnknown == newStateValue) {
123         mStateMap.erase(primaryKey);
124     }
125 
126     // Update state map for non-nested counting case.
127     // Every state event triggers a state overwrite.
128     if (!nested) {
129         stateValueInfo->state = newStateValue;
130         stateValueInfo->count = 1;
131 
132         // Notify listeners if state has changed.
133         if (oldStateValue != newStateValue) {
134             notifyListeners(eventTimeNs, primaryKey, oldState, newState);
135         }
136         return;
137     }
138 
139     // Update state map for nested counting case.
140     //
141     // Nested counting is only allowed for binary state events such as ON/OFF or
142     // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
143     // events: ON, ON, OFF. The state will still be ON until we see the same
144     // number of OFF events as ON events.
145     //
146     // In atoms.proto, a state atom with nested counting enabled
147     // must only have 2 states. There is no enforcemnt here of this requirement.
148     // The atom must be logged correctly.
149     if (kStateUnknown == newStateValue) {
150         if (kStateUnknown != oldStateValue) {
151             notifyListeners(eventTimeNs, primaryKey, oldState, newState);
152         }
153     } else if (oldStateValue == kStateUnknown) {
154         stateValueInfo->state = newStateValue;
155         stateValueInfo->count = 1;
156         notifyListeners(eventTimeNs, primaryKey, oldState, newState);
157     } else if (oldStateValue == newStateValue) {
158         stateValueInfo->count++;
159     } else if (--stateValueInfo->count == 0) {
160         stateValueInfo->state = newStateValue;
161         stateValueInfo->count = 1;
162         notifyListeners(eventTimeNs, primaryKey, oldState, newState);
163     }
164 }
165 
notifyListeners(const int64_t eventTimeNs,const HashableDimensionKey & primaryKey,const FieldValue & oldState,const FieldValue & newState)166 void StateTracker::notifyListeners(const int64_t eventTimeNs,
167                                    const HashableDimensionKey& primaryKey,
168                                    const FieldValue& oldState, const FieldValue& newState) {
169     for (auto l : mListeners) {
170         auto sl = l.promote();
171         if (sl != nullptr) {
172             sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
173         }
174     }
175 }
176 
getStateFieldValueFromLogEvent(const LogEvent & event,FieldValue * output)177 bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
178     const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
179     if (-1 == exclusiveStateFieldIndex) {
180         ALOGE("error extracting state from log event. Missing exclusive state field.");
181         return false;
182     }
183 
184     *output = event.getValues()[exclusiveStateFieldIndex];
185     return true;
186 }
187 
188 }  // namespace statsd
189 }  // namespace os
190 }  // namespace android
191