1 /*
2  * Copyright (C) 2017 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 false  // STOPSHIP if true
18 #include "Log.h"
19 
20 #include "MetricProducer.h"
21 
22 #include "../guardrail/StatsdStats.h"
23 #include "state/StateTracker.h"
24 
25 using android::util::FIELD_COUNT_REPEATED;
26 using android::util::FIELD_TYPE_ENUM;
27 using android::util::FIELD_TYPE_INT32;
28 using android::util::FIELD_TYPE_INT64;
29 using android::util::FIELD_TYPE_MESSAGE;
30 using android::util::ProtoOutputStream;
31 
32 namespace android {
33 namespace os {
34 namespace statsd {
35 
36 
37 // for ActiveMetric
38 const int FIELD_ID_ACTIVE_METRIC_ID = 1;
39 const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2;
40 
41 // for ActiveEventActivation
42 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
43 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
44 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
45 
MetricProducer(const int64_t & metricId,const ConfigKey & key,const int64_t timeBaseNs,const int conditionIndex,const vector<ConditionState> & initialConditionCache,const sp<ConditionWizard> & wizard,const std::unordered_map<int,std::shared_ptr<Activation>> & eventActivationMap,const std::unordered_map<int,std::vector<std::shared_ptr<Activation>>> & eventDeactivationMap,const vector<int> & slicedStateAtoms,const unordered_map<int,unordered_map<int,int64_t>> & stateGroupMap)46 MetricProducer::MetricProducer(
47         const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
48         const int conditionIndex, const vector<ConditionState>& initialConditionCache,
49         const sp<ConditionWizard>& wizard,
50         const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
51         const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
52                 eventDeactivationMap,
53         const vector<int>& slicedStateAtoms,
54         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
55     : mMetricId(metricId),
56       mConfigKey(key),
57       mTimeBaseNs(timeBaseNs),
58       mCurrentBucketStartTimeNs(timeBaseNs),
59       mCurrentBucketNum(0),
60       mCondition(initialCondition(conditionIndex, initialConditionCache)),
61       mConditionTrackerIndex(conditionIndex),
62       mConditionSliced(false),
63       mWizard(wizard),
64       mContainANYPositionInDimensionsInWhat(false),
65       mSliceByPositionALL(false),
66       mHasLinksToAllConditionDimensionsInTracker(false),
67       mEventActivationMap(eventActivationMap),
68       mEventDeactivationMap(eventDeactivationMap),
69       mIsActive(mEventActivationMap.empty()),
70       mSlicedStateAtoms(slicedStateAtoms),
71       mStateGroupMap(stateGroupMap) {
72 }
73 
onMatchedLogEventLocked(const size_t matcherIndex,const LogEvent & event)74 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
75     if (!mIsActive) {
76         return;
77     }
78     int64_t eventTimeNs = event.GetElapsedTimestampNs();
79     // this is old event, maybe statsd restarted?
80     if (eventTimeNs < mTimeBaseNs) {
81         return;
82     }
83 
84     bool condition;
85     ConditionKey conditionKey;
86     if (mConditionSliced) {
87         for (const auto& link : mMetric2ConditionLinks) {
88             getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
89         }
90         auto conditionState =
91             mWizard->query(mConditionTrackerIndex, conditionKey,
92                            !mHasLinksToAllConditionDimensionsInTracker);
93         condition = (conditionState == ConditionState::kTrue);
94     } else {
95         // TODO: The unknown condition state is not handled here, we should fix it.
96         condition = mCondition == ConditionState::kTrue;
97     }
98 
99     // Stores atom id to primary key pairs for each state atom that the metric is
100     // sliced by.
101     std::map<int32_t, HashableDimensionKey> statePrimaryKeys;
102 
103     // For states with primary fields, use MetricStateLinks to get the primary
104     // field values from the log event. These values will form a primary key
105     // that will be used to query StateTracker for the correct state value.
106     for (const auto& stateLink : mMetric2StateLinks) {
107         getDimensionForState(event.getValues(), stateLink,
108                              &statePrimaryKeys[stateLink.stateAtomId]);
109     }
110 
111     // For each sliced state, query StateTracker for the state value using
112     // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
113     //
114     // Expected functionality: for any case where the MetricStateLinks are
115     // initialized incorrectly (ex. # of state links != # of primary fields, no
116     // links are provided for a state with primary fields, links are provided
117     // in the wrong order, etc.), StateTracker will simply return kStateUnknown
118     // when queried using an incorrect key.
119     HashableDimensionKey stateValuesKey;
120     for (auto atomId : mSlicedStateAtoms) {
121         FieldValue value;
122         if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
123             // found a primary key for this state, query using the key
124             queryStateValue(atomId, statePrimaryKeys[atomId], &value);
125         } else {
126             // if no MetricStateLinks exist for this state atom,
127             // query using the default dimension key (empty HashableDimensionKey)
128             queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
129         }
130         mapStateValue(atomId, &value);
131         stateValuesKey.addValue(value);
132     }
133 
134     HashableDimensionKey dimensionInWhat;
135     filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
136     MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
137     onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
138                                     statePrimaryKeys);
139 }
140 
evaluateActiveStateLocked(int64_t elapsedTimestampNs)141 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
142     bool isActive = mEventActivationMap.empty();
143     for (auto& it : mEventActivationMap) {
144         if (it.second->state == ActivationState::kActive &&
145             elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) {
146             it.second->state = ActivationState::kNotActive;
147         }
148         if (it.second->state == ActivationState::kActive) {
149             isActive = true;
150         }
151     }
152     return isActive;
153 }
154 
flushIfExpire(int64_t elapsedTimestampNs)155 void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
156     std::lock_guard<std::mutex> lock(mMutex);
157     if (!mIsActive) {
158         return;
159     }
160     mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
161     if (!mIsActive) {
162         onActiveStateChangedLocked(elapsedTimestampNs);
163     }
164 }
165 
activateLocked(int activationTrackerIndex,int64_t elapsedTimestampNs)166 void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
167     auto it = mEventActivationMap.find(activationTrackerIndex);
168     if (it == mEventActivationMap.end()) {
169         return;
170     }
171     auto& activation = it->second;
172     if (ACTIVATE_ON_BOOT == activation->activationType) {
173         if (ActivationState::kNotActive == activation->state) {
174             activation->state = ActivationState::kActiveOnBoot;
175         }
176         // If the Activation is already active or set to kActiveOnBoot, do nothing.
177         return;
178     }
179     activation->start_ns = elapsedTimestampNs;
180     activation->state = ActivationState::kActive;
181     bool oldActiveState = mIsActive;
182     mIsActive = true;
183     if (!oldActiveState) { // Metric went from not active to active.
184         onActiveStateChangedLocked(elapsedTimestampNs);
185     }
186 }
187 
cancelEventActivationLocked(int deactivationTrackerIndex)188 void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
189     auto it = mEventDeactivationMap.find(deactivationTrackerIndex);
190     if (it == mEventDeactivationMap.end()) {
191         return;
192     }
193     for (auto activationToCancelIt : it->second)  {
194         activationToCancelIt->state = ActivationState::kNotActive;
195     }
196 }
197 
loadActiveMetricLocked(const ActiveMetric & activeMetric,int64_t currentTimeNs)198 void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric,
199                                             int64_t currentTimeNs) {
200     if (mEventActivationMap.size() == 0) {
201         return;
202     }
203     for (int i = 0; i < activeMetric.activation_size(); i++) {
204         const auto& activeEventActivation = activeMetric.activation(i);
205         auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index());
206         if (it == mEventActivationMap.end()) {
207             ALOGE("Saved event activation not found");
208             continue;
209         }
210         auto& activation = it->second;
211         // If the event activation does not have a state, assume it is active.
212         if (!activeEventActivation.has_state() ||
213                 activeEventActivation.state() == ActiveEventActivation::ACTIVE) {
214             // We don't want to change the ttl for future activations, so we set the start_ns
215             // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos
216             activation->start_ns =
217                 currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns;
218             activation->state = ActivationState::kActive;
219             mIsActive = true;
220         } else if (activeEventActivation.state() == ActiveEventActivation::ACTIVATE_ON_BOOT) {
221             activation->state = ActivationState::kActiveOnBoot;
222         }
223     }
224 }
225 
writeActiveMetricToProtoOutputStream(int64_t currentTimeNs,const DumpReportReason reason,ProtoOutputStream * proto)226 void MetricProducer::writeActiveMetricToProtoOutputStream(
227         int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
228     proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId);
229     for (auto& it : mEventActivationMap) {
230         const int atom_matcher_index = it.first;
231         const std::shared_ptr<Activation>& activation = it.second;
232 
233         if (ActivationState::kNotActive == activation->state ||
234                 (ActivationState::kActive == activation->state &&
235                  activation->start_ns + activation->ttl_ns < currentTimeNs)) {
236             continue;
237         }
238 
239         const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
240                 FIELD_ID_ACTIVE_METRIC_ACTIVATION);
241         proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX,
242                 atom_matcher_index);
243         if (ActivationState::kActive == activation->state) {
244             const int64_t remainingTtlNs =
245                     activation->start_ns + activation->ttl_ns - currentTimeNs;
246             proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
247                     (long long)remainingTtlNs);
248             proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
249                     ActiveEventActivation::ACTIVE);
250 
251         } else if (ActivationState::kActiveOnBoot == activation->state) {
252             if (reason == DEVICE_SHUTDOWN || reason == TERMINATION_SIGNAL_RECEIVED) {
253                 proto->write(
254                         FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS,
255                         (long long)activation->ttl_ns);
256                 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
257                                     ActiveEventActivation::ACTIVE);
258             } else if (reason == STATSCOMPANION_DIED) {
259                 // We are saving because of system server death, not due to a device shutdown.
260                 // Next time we load, we do not want to activate metrics that activate on boot.
261                 proto->write(FIELD_TYPE_ENUM | FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE,
262                                                     ActiveEventActivation::ACTIVATE_ON_BOOT);
263             }
264         }
265         proto->end(activationToken);
266     }
267 }
268 
queryStateValue(const int32_t atomId,const HashableDimensionKey & queryKey,FieldValue * value)269 void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
270                                      FieldValue* value) {
271     if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
272         value->mValue = Value(StateTracker::kStateUnknown);
273         value->mField.setTag(atomId);
274         ALOGW("StateTracker not found for state atom %d", atomId);
275         return;
276     }
277 }
278 
mapStateValue(const int32_t atomId,FieldValue * value)279 void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
280     // check if there is a state map for this atom
281     auto atomIt = mStateGroupMap.find(atomId);
282     if (atomIt == mStateGroupMap.end()) {
283         return;
284     }
285     auto valueIt = atomIt->second.find(value->mValue.int_value);
286     if (valueIt == atomIt->second.end()) {
287         // state map exists, but value was not put in a state group
288         // so set mValue to kStateUnknown
289         // TODO(tsaichristine): handle incomplete state maps
290         value->mValue.setInt(StateTracker::kStateUnknown);
291     } else {
292         // set mValue to group_id
293         value->mValue.setLong(valueIt->second);
294     }
295 }
296 
getUnknownStateKey()297 HashableDimensionKey MetricProducer::getUnknownStateKey() {
298     HashableDimensionKey stateKey;
299     for (auto atom : mSlicedStateAtoms) {
300         FieldValue fieldValue;
301         fieldValue.mField.setTag(atom);
302         fieldValue.mValue.setInt(StateTracker::kStateUnknown);
303         stateKey.addValue(fieldValue);
304     }
305     return stateKey;
306 }
307 
buildDropEvent(const int64_t dropTimeNs,const BucketDropReason reason)308 DropEvent MetricProducer::buildDropEvent(const int64_t dropTimeNs, const BucketDropReason reason) {
309     DropEvent event;
310     event.reason = reason;
311     event.dropTimeNs = dropTimeNs;
312     return event;
313 }
314 
maxDropEventsReached()315 bool MetricProducer::maxDropEventsReached() {
316     return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
317 }
318 
319 }  // namespace statsd
320 }  // namespace os
321 }  // namespace android
322