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 #define DEBUG false  // STOPSHIP if true
17 #include "Log.h"
18 
19 #include "HashableDimensionKey.h"
20 #include "FieldValue.h"
21 
22 namespace android {
23 namespace os {
24 namespace statsd {
25 
26 using std::string;
27 using std::vector;
28 using android::base::StringPrintf;
29 
30 // These constants must be kept in sync with those in StatsDimensionsValue.java
31 const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
32 const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
33 const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4;
34 // const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because
35 // unused -- statsd does not correctly support bool types)
36 const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
37 const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
38 
39 /**
40  * Recursive helper function that populates a parent StatsDimensionsValueParcel
41  * with children StatsDimensionsValueParcels.
42  *
43  * \param parent parcel that will be populated with children
44  * \param childDepth depth of children FieldValues
45  * \param childPrefix expected FieldValue prefix of children
46  * \param dims vector of FieldValues stored by HashableDimensionKey
47  * \param index position in dims to start reading children from
48  */
populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel & parent,int childDepth,int childPrefix,const vector<FieldValue> & dims,size_t & index)49 static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent,
50                                                        int childDepth, int childPrefix,
51                                                        const vector<FieldValue>& dims,
52                                                        size_t& index) {
53     if (childDepth > 2) {
54         ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel.");
55         return;
56     }
57 
58     while (index < dims.size()) {
59         const FieldValue& dim = dims[index];
60         int fieldDepth = dim.mField.getDepth();
61         int fieldPrefix = dim.mField.getPrefix(childDepth);
62 
63         StatsDimensionsValueParcel child;
64         child.field = dim.mField.getPosAtDepth(childDepth);
65 
66         if (fieldDepth == childDepth && fieldPrefix == childPrefix) {
67             switch (dim.mValue.getType()) {
68                 case INT:
69                     child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE;
70                     child.intValue = dim.mValue.int_value;
71                     break;
72                 case LONG:
73                     child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE;
74                     child.longValue = dim.mValue.long_value;
75                     break;
76                 case FLOAT:
77                     child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE;
78                     child.floatValue = dim.mValue.float_value;
79                     break;
80                 case STRING:
81                     child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
82                     child.stringValue = dim.mValue.str_value;
83                     break;
84                 default:
85                     ALOGE("Encountered FieldValue with unsupported value type.");
86                     break;
87             }
88             index++;
89             parent.tupleValue.push_back(child);
90         } else if (fieldDepth > childDepth && fieldPrefix == childPrefix) {
91             // This FieldValue is not a child of the current parent, but it is
92             // an indirect descendant. Thus, create a direct child of TUPLE_TYPE
93             // and recurse to parcel the indirect descendants.
94             child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
95             populateStatsDimensionsValueParcelChildren(child, childDepth + 1,
96                                                        dim.mField.getPrefix(childDepth + 1), dims,
97                                                        index);
98             parent.tupleValue.push_back(child);
99         } else {
100             return;
101         }
102     }
103 }
104 
toStatsDimensionsValueParcel() const105 StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const {
106     StatsDimensionsValueParcel root;
107     if (mValues.size() == 0) {
108         return root;
109     }
110 
111     root.field = mValues[0].mField.getTag();
112     root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
113 
114     // Children of the root correspond to top-level (depth = 0) FieldValues.
115     int childDepth = 0;
116     int childPrefix = 0;
117     size_t index = 0;
118     populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index);
119 
120     return root;
121 }
122 
hashDimension(const HashableDimensionKey & value)123 android::hash_t hashDimension(const HashableDimensionKey& value) {
124     android::hash_t hash = 0;
125     for (const auto& fieldValue : value.getValues()) {
126         hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField()));
127         hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag()));
128         hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType()));
129         switch (fieldValue.mValue.getType()) {
130             case INT:
131                 hash = android::JenkinsHashMix(hash,
132                                                android::hash_type(fieldValue.mValue.int_value));
133                 break;
134             case LONG:
135                 hash = android::JenkinsHashMix(hash,
136                                                android::hash_type(fieldValue.mValue.long_value));
137                 break;
138             case STRING:
139                 hash = android::JenkinsHashMix(hash, static_cast<uint32_t>(std::hash<std::string>()(
140                                                              fieldValue.mValue.str_value)));
141                 break;
142             case FLOAT: {
143                 hash = android::JenkinsHashMix(hash,
144                                                android::hash_type(fieldValue.mValue.float_value));
145                 break;
146             }
147             default:
148                 break;
149         }
150     }
151     return JenkinsHashWhiten(hash);
152 }
153 
filterValues(const Matcher & matcherField,const vector<FieldValue> & values,FieldValue * output)154 bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values,
155                   FieldValue* output) {
156     for (const auto& value : values) {
157         if (value.mField.matches(matcherField)) {
158             (*output) = value;
159             return true;
160         }
161     }
162     return false;
163 }
164 
filterValues(const vector<Matcher> & matcherFields,const vector<FieldValue> & values,HashableDimensionKey * output)165 bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
166                   HashableDimensionKey* output) {
167     size_t num_matches = 0;
168     for (const auto& value : values) {
169         for (size_t i = 0; i < matcherFields.size(); ++i) {
170             const auto& matcher = matcherFields[i];
171             if (value.mField.matches(matcher)) {
172                 output->addValue(value);
173                 output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
174                 output->mutableValue(num_matches)->mField.setField(
175                     value.mField.getField() & matcher.mMask);
176                 num_matches++;
177             }
178         }
179     }
180     return num_matches > 0;
181 }
182 
filterPrimaryKey(const std::vector<FieldValue> & values,HashableDimensionKey * output)183 bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) {
184     size_t num_matches = 0;
185     const int32_t simpleFieldMask = 0xff7f0000;
186     const int32_t attributionUidFieldMask = 0xff7f7f7f;
187     for (const auto& value : values) {
188         if (value.mAnnotations.isPrimaryField()) {
189             output->addValue(value);
190             output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
191             const int32_t mask =
192                     isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask;
193             output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask);
194             num_matches++;
195         }
196     }
197     return num_matches > 0;
198 }
199 
filterGaugeValues(const std::vector<Matcher> & matcherFields,const std::vector<FieldValue> & values,std::vector<FieldValue> * output)200 void filterGaugeValues(const std::vector<Matcher>& matcherFields,
201                        const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
202     for (const auto& field : matcherFields) {
203         for (const auto& value : values) {
204             if (value.mField.matches(field)) {
205                 output->push_back(value);
206             }
207         }
208     }
209 }
210 
getDimensionForCondition(const std::vector<FieldValue> & eventValues,const Metric2Condition & links,HashableDimensionKey * conditionDimension)211 void getDimensionForCondition(const std::vector<FieldValue>& eventValues,
212                               const Metric2Condition& links,
213                               HashableDimensionKey* conditionDimension) {
214     // Get the dimension first by using dimension from what.
215     filterValues(links.metricFields, eventValues, conditionDimension);
216 
217     size_t count = conditionDimension->getValues().size();
218     if (count != links.conditionFields.size()) {
219         return;
220     }
221 
222     for (size_t i = 0; i < count; i++) {
223         conditionDimension->mutableValue(i)->mField.setField(
224                 links.conditionFields[i].mMatcher.getField());
225         conditionDimension->mutableValue(i)->mField.setTag(
226                 links.conditionFields[i].mMatcher.getTag());
227     }
228 }
229 
getDimensionForState(const std::vector<FieldValue> & eventValues,const Metric2State & link,HashableDimensionKey * statePrimaryKey)230 void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
231                           HashableDimensionKey* statePrimaryKey) {
232     // First, get the dimension from the event using the "what" fields from the
233     // MetricStateLinks.
234     filterValues(link.metricFields, eventValues, statePrimaryKey);
235 
236     // Then check that the statePrimaryKey size equals the number of state fields
237     size_t count = statePrimaryKey->getValues().size();
238     if (count != link.stateFields.size()) {
239         return;
240     }
241 
242     // For each dimension Value in the statePrimaryKey, set the field and tag
243     // using the state atom fields from MetricStateLinks.
244     for (size_t i = 0; i < count; i++) {
245         statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField());
246         statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag());
247     }
248 }
249 
containsLinkedStateValues(const HashableDimensionKey & whatKey,const HashableDimensionKey & primaryKey,const vector<Metric2State> & stateLinks,const int32_t stateAtomId)250 bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
251                                const HashableDimensionKey& primaryKey,
252                                const vector<Metric2State>& stateLinks, const int32_t stateAtomId) {
253     if (whatKey.getValues().size() < primaryKey.getValues().size()) {
254         ALOGE("Contains linked values false: whatKey is too small");
255         return false;
256     }
257 
258     for (const auto& primaryValue : primaryKey.getValues()) {
259         bool found = false;
260         for (const auto& whatValue : whatKey.getValues()) {
261             if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) &&
262                 primaryValue.mValue == whatValue.mValue) {
263                 found = true;
264                 break;
265             }
266         }
267         if (!found) {
268             return false;
269         }
270     }
271     return true;
272 }
273 
linked(const vector<Metric2State> & stateLinks,const int32_t stateAtomId,const Field & stateField,const Field & metricField)274 bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId,
275             const Field& stateField, const Field& metricField) {
276     for (auto stateLink : stateLinks) {
277         if (stateLink.stateAtomId != stateAtomId) {
278             continue;
279         }
280 
281         for (size_t i = 0; i < stateLink.stateFields.size(); i++) {
282             if (stateLink.stateFields[i].mMatcher == stateField &&
283                 stateLink.metricFields[i].mMatcher == metricField) {
284                 return true;
285             }
286         }
287     }
288     return false;
289 }
290 
LessThan(const vector<FieldValue> & s1,const vector<FieldValue> & s2)291 bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
292     if (s1.size() != s2.size()) {
293         return s1.size() < s2.size();
294     }
295 
296     size_t count = s1.size();
297     for (size_t i = 0; i < count; i++) {
298         if (s1[i] != s2[i]) {
299             return s1[i] < s2[i];
300         }
301     }
302     return false;
303 }
304 
operator !=(const HashableDimensionKey & that) const305 bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const {
306     return !((*this) == that);
307 }
308 
operator ==(const HashableDimensionKey & that) const309 bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
310     if (mValues.size() != that.getValues().size()) {
311         return false;
312     }
313     size_t count = mValues.size();
314     for (size_t i = 0; i < count; i++) {
315         if (mValues[i] != (that.getValues())[i]) {
316             return false;
317         }
318     }
319     return true;
320 };
321 
operator <(const HashableDimensionKey & that) const322 bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
323     return LessThan(getValues(), that.getValues());
324 };
325 
contains(const HashableDimensionKey & that) const326 bool HashableDimensionKey::contains(const HashableDimensionKey& that) const {
327     if (mValues.size() < that.getValues().size()) {
328         return false;
329     }
330 
331     if (mValues.size() == that.getValues().size()) {
332         return (*this) == that;
333     }
334 
335     for (const auto& value : that.getValues()) {
336         bool found = false;
337         for (const auto& myValue : mValues) {
338             if (value.mField == myValue.mField && value.mValue == myValue.mValue) {
339                 found = true;
340                 break;
341             }
342         }
343         if (!found) {
344             return false;
345         }
346     }
347 
348     return true;
349 }
350 
toString() const351 string HashableDimensionKey::toString() const {
352     std::string output;
353     for (const auto& value : mValues) {
354         output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(),
355                                value.mValue.toString().c_str());
356     }
357     return output;
358 }
359 
operator ==(const MetricDimensionKey & that) const360 bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
361     return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
362            mStateValuesKey == that.getStateValuesKey();
363 };
364 
toString() const365 string MetricDimensionKey::toString() const {
366     return mDimensionKeyInWhat.toString() + mStateValuesKey.toString();
367 }
368 
operator <(const MetricDimensionKey & that) const369 bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
370     if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) {
371         return true;
372     } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) {
373         return false;
374     }
375 
376     return mStateValuesKey < that.getStateValuesKey();
377 }
378 
379 }  // namespace statsd
380 }  // namespace os
381 }  // namespace android
382