blob: b7f314a819df868fdbc771d5f03dcf5e29abbcd3 [file] [log] [blame]
tsaichristine10978642019-09-10 14:12:49 -07001/*
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
24namespace android {
25namespace os {
26namespace statsd {
27
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070028StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
tsaichristine10978642019-09-10 14:12:49 -070029}
30
31void StateTracker::onLogEvent(const LogEvent& event) {
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070032 const int64_t eventTimeNs = event.GetElapsedTimestampNs();
tsaichristine8d73dc92019-12-06 02:11:02 -080033
34 // Parse event for primary field values i.e. primary key.
tsaichristine10978642019-09-10 14:12:49 -070035 HashableDimensionKey primaryKey;
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070036 filterPrimaryKey(event.getValues(), &primaryKey);
37
38 FieldValue stateValue;
39 if (!getStateFieldValueFromLogEvent(event, &stateValue)) {
40 ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
41 clearStateForPrimaryKey(eventTimeNs, primaryKey);
42 return;
tsaichristine10978642019-09-10 14:12:49 -070043 }
44
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070045 mField.setField(stateValue.mField.getField());
46
47 if (stateValue.mValue.getType() != INT) {
tsaichristine69000e62019-10-18 17:34:52 -070048 ALOGE("StateTracker error extracting state from log event. Type: %d",
49 stateValue.mValue.getType());
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070050 clearStateForPrimaryKey(eventTimeNs, primaryKey);
tsaichristine10978642019-09-10 14:12:49 -070051 return;
52 }
tsaichristine10978642019-09-10 14:12:49 -070053
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070054 const int32_t resetState = stateValue.mAnnotations.getResetState();
55 if (resetState != -1) {
56 VLOG("StateTracker new reset state: %d", resetState);
57 handleReset(eventTimeNs, resetState);
tsaichristine5adc7e02020-01-14 17:07:39 -080058 return;
tsaichristine10978642019-09-10 14:12:49 -070059 }
60
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070061 const int32_t newState = stateValue.mValue.int_value;
62 const bool nested = stateValue.mAnnotations.isNested();
63 StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
64 updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
tsaichristine10978642019-09-10 14:12:49 -070065}
66
67void StateTracker::registerListener(wp<StateListener> listener) {
68 mListeners.insert(listener);
69}
70
71void StateTracker::unregisterListener(wp<StateListener> listener) {
72 mListeners.erase(listener);
73}
74
tsaichristine69000e62019-10-18 17:34:52 -070075bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070076 output->mField = mField;
tsaichristine69000e62019-10-18 17:34:52 -070077
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070078 if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
79 output->mValue = it->second.state;
80 return true;
tsaichristine10978642019-09-10 14:12:49 -070081 }
tsaichristine69000e62019-10-18 17:34:52 -070082
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070083 // Set the state value to kStateUnknown if query key is not found in state map.
84 output->mValue = kStateUnknown;
tsaichristine69000e62019-10-18 17:34:52 -070085 return false;
tsaichristine10978642019-09-10 14:12:49 -070086}
87
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070088void StateTracker::handleReset(const int64_t eventTimeNs, const int32_t newState) {
tsaichristine10978642019-09-10 14:12:49 -070089 VLOG("StateTracker handle reset");
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070090 for (auto& [primaryKey, stateValueInfo] : mStateMap) {
91 updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
92 false /* nested; treat this state change as not nested */,
93 &stateValueInfo);
tsaichristine10978642019-09-10 14:12:49 -070094 }
95}
96
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -070097void 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.
tsaichristine10978642019-09-10 14:12:49 -0700105 if (it != mStateMap.end()) {
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -0700106 updateStateForPrimaryKey(eventTimeNs, primaryKey, kStateUnknown,
107 false /* nested; treat this state change as not nested */,
108 &it->second);
109 }
110}
111
112void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
113 const HashableDimensionKey& primaryKey,
114 const int32_t newState, const bool nested,
115 StateValueInfo* stateValueInfo) {
116 const int32_t oldState = stateValueInfo->state;
117
118 if (kStateUnknown == newState) {
119 mStateMap.erase(primaryKey);
tsaichristine10978642019-09-10 14:12:49 -0700120 }
121
tsaichristine5adc7e02020-01-14 17:07:39 -0800122 // Update state map for non-nested counting case.
123 // Every state event triggers a state overwrite.
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -0700124 if (!nested) {
125 stateValueInfo->state = newState;
126 stateValueInfo->count = 1;
127
128 // Notify listeners if state has changed.
129 if (oldState != newState) {
130 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
tsaichristine5adc7e02020-01-14 17:07:39 -0800131 }
tsaichristine5adc7e02020-01-14 17:07:39 -0800132 return;
tsaichristine10978642019-09-10 14:12:49 -0700133 }
tsaichristine10978642019-09-10 14:12:49 -0700134
tsaichristine5adc7e02020-01-14 17:07:39 -0800135 // Update state map for nested counting case.
136 //
137 // Nested counting is only allowed for binary state events such as ON/OFF or
138 // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
139 // events: ON, ON, OFF. The state will still be ON until we see the same
140 // number of OFF events as ON events.
141 //
142 // In atoms.proto, a state atom with nested counting enabled
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -0700143 // must only have 2 states. There is no enforcemnt here of this requirement.
144 // The atom must be logged correctly.
145 if (kStateUnknown == newState) {
146 if (kStateUnknown != oldState) {
147 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
tsaichristine5adc7e02020-01-14 17:07:39 -0800148 }
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -0700149 } else if (oldState == kStateUnknown) {
150 stateValueInfo->state = newState;
151 stateValueInfo->count = 1;
152 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
153 } else if (oldState == newState) {
154 stateValueInfo->count++;
155 } else if (--stateValueInfo->count == 0) {
156 stateValueInfo->state = newState;
157 stateValueInfo->count = 1;
158 notifyListeners(eventTimeNs, primaryKey, oldState, newState);
tsaichristine5adc7e02020-01-14 17:07:39 -0800159 }
tsaichristine10978642019-09-10 14:12:49 -0700160}
161
Muhammad Qureshibfc4bdb2020-04-08 06:26:49 -0700162void StateTracker::notifyListeners(const int64_t eventTimeNs,
163 const HashableDimensionKey& primaryKey, const int32_t oldState,
164 const int32_t newState) {
165 for (auto l : mListeners) {
166 auto sl = l.promote();
167 if (sl != nullptr) {
168 sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
169 }
170 }
171}
172
173bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
174 const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
175 if (-1 == exclusiveStateFieldIndex) {
176 ALOGE("error extracting state from log event. Missing exclusive state field.");
177 return false;
178 }
179
180 *output = event.getValues()[exclusiveStateFieldIndex];
181 return true;
182}
183
tsaichristine10978642019-09-10 14:12:49 -0700184} // namespace statsd
185} // namespace os
186} // namespace android