1 /*
2  * Copyright (C) 2021 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 #define LOG_TAG "StatsAidl"
19 
20 #define VLOG(...) \
21     if (DEBUG) ALOGD(__VA_ARGS__);
22 
23 #include "StatsAidl.h"
24 
25 #include <Counter.h>
26 #include <log/log.h>
27 #include <stats_annotations.h>
28 #include <stats_event.h>
29 #include <statslog.h>
30 
31 #include <unordered_map>
32 
33 namespace {
34     static const char* g_AtomErrorMetricName =
35         "statsd_errors.value_report_vendor_atom_errors_count";
36 }
37 
38 namespace aidl {
39 namespace android {
40 namespace frameworks {
41 namespace stats {
42 
43 using ::android::expresslog::Counter;
44 
45 template <typename E>
to_underlying(E e)46 constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept {
47     return static_cast<typename std::underlying_type<E>::type>(e);
48 }
49 
StatsHal()50 StatsHal::StatsHal() {
51 }
52 
write_annotation(AStatsEvent * event,const Annotation & annotation)53 bool write_annotation(AStatsEvent* event, const Annotation& annotation) {
54     switch (annotation.value.getTag()) {
55         case AnnotationValue::boolValue: {
56             AStatsEvent_addBoolAnnotation(event, to_underlying(annotation.annotationId),
57                                           annotation.value.get<AnnotationValue::boolValue>());
58             break;
59         }
60         case AnnotationValue::intValue: {
61             AStatsEvent_addInt32Annotation(event, to_underlying(annotation.annotationId),
62                                            annotation.value.get<AnnotationValue::intValue>());
63             break;
64         }
65         default: {
66             return false;
67         }
68     }
69     return true;
70 }
71 
write_atom_annotations(AStatsEvent * event,const std::vector<std::optional<Annotation>> & annotations)72 bool write_atom_annotations(AStatsEvent* event,
73                             const std::vector<std::optional<Annotation>>& annotations) {
74     for (const auto& atomAnnotation : annotations) {
75         if (!atomAnnotation) {
76             return false;
77         }
78         if (!write_annotation(event, *atomAnnotation)) {
79             return false;
80         }
81     }
82     return true;
83 }
84 
write_field_annotations(AStatsEvent * event,const std::vector<Annotation> & annotations)85 bool write_field_annotations(AStatsEvent* event, const std::vector<Annotation>& annotations) {
86     for (const auto& fieldAnnotation : annotations) {
87         if (!write_annotation(event, fieldAnnotation)) {
88             return false;
89         }
90     }
91     return true;
92 }
93 
reportVendorAtom(const VendorAtom & vendorAtom)94 ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
95     if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
96         ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId);
97         Counter::logIncrement(g_AtomErrorMetricName);
98         return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
99                 -1, "Not a valid vendor atom ID");
100     }
101     if (vendorAtom.reverseDomainName.length() > 50) {
102         ALOGE("Vendor atom reverse domain name %s is too long.",
103               vendorAtom.reverseDomainName.c_str());
104         Counter::logIncrement(g_AtomErrorMetricName);
105         return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
106                 -1, "Vendor atom reverse domain name is too long");
107     }
108     AStatsEvent* event = AStatsEvent_obtain();
109     AStatsEvent_setAtomId(event, vendorAtom.atomId);
110 
111     if (vendorAtom.atomAnnotations) {
112         if (!write_atom_annotations(event, *vendorAtom.atomAnnotations)) {
113             AStatsEvent_release(event);
114             ALOGE("Atom ID %ld has incompatible atom level annotation", (long)vendorAtom.atomId);
115             Counter::logIncrement(g_AtomErrorMetricName);
116             return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
117                     -1, "invalid atom annotation");
118         }
119     }
120 
121     // populate map for quickier access for VendorAtomValue associated annotations by value index
122     std::unordered_map<int, int> fieldIndexToAnnotationSetMap;
123     if (vendorAtom.valuesAnnotations) {
124         const std::vector<std::optional<AnnotationSet>>& valuesAnnotations =
125                 *vendorAtom.valuesAnnotations;
126         for (int i = 0; i < valuesAnnotations.size(); i++) {
127             if (valuesAnnotations[i]) {
128                 fieldIndexToAnnotationSetMap[valuesAnnotations[i]->valueIndex] = i;
129             }
130         }
131     }
132 
133     AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str());
134     size_t atomValueIdx = 0;
135     for (const auto& atomValue : vendorAtom.values) {
136         switch (atomValue.getTag()) {
137             case VendorAtomValue::intValue:
138                 AStatsEvent_writeInt32(event, atomValue.get<VendorAtomValue::intValue>());
139                 break;
140             case VendorAtomValue::longValue:
141                 AStatsEvent_writeInt64(event, atomValue.get<VendorAtomValue::longValue>());
142                 break;
143             case VendorAtomValue::floatValue:
144                 AStatsEvent_writeFloat(event, atomValue.get<VendorAtomValue::floatValue>());
145                 break;
146             case VendorAtomValue::stringValue:
147                 AStatsEvent_writeString(event,
148                                         atomValue.get<VendorAtomValue::stringValue>().c_str());
149                 break;
150             case VendorAtomValue::boolValue:
151                 AStatsEvent_writeBool(event, atomValue.get<VendorAtomValue::boolValue>());
152                 break;
153             case VendorAtomValue::repeatedIntValue: {
154                 const std::optional<std::vector<int>>& repeatedIntValue =
155                         atomValue.get<VendorAtomValue::repeatedIntValue>();
156                 if (!repeatedIntValue) {
157                     AStatsEvent_writeInt32Array(event, {}, 0);
158                     break;
159                 }
160                 AStatsEvent_writeInt32Array(event, repeatedIntValue->data(),
161                                             repeatedIntValue->size());
162                 break;
163             }
164             case VendorAtomValue::repeatedLongValue: {
165                 const std::optional<std::vector<int64_t>>& repeatedLongValue =
166                         atomValue.get<VendorAtomValue::repeatedLongValue>();
167                 if (!repeatedLongValue) {
168                     AStatsEvent_writeInt64Array(event, {}, 0);
169                     break;
170                 }
171                 AStatsEvent_writeInt64Array(event, repeatedLongValue->data(),
172                                             repeatedLongValue->size());
173                 break;
174             }
175             case VendorAtomValue::repeatedFloatValue: {
176                 const std::optional<std::vector<float>>& repeatedFloatValue =
177                         atomValue.get<VendorAtomValue::repeatedFloatValue>();
178                 if (!repeatedFloatValue) {
179                     AStatsEvent_writeFloatArray(event, {}, 0);
180                     break;
181                 }
182                 AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(),
183                                             repeatedFloatValue->size());
184                 break;
185             }
186             case VendorAtomValue::repeatedStringValue: {
187                 const std::optional<std::vector<std::optional<std::string>>>& repeatedStringValue =
188                         atomValue.get<VendorAtomValue::repeatedStringValue>();
189                 if (!repeatedStringValue) {
190                     AStatsEvent_writeStringArray(event, {}, 0);
191                     break;
192                 }
193                 const std::vector<std::optional<std::string>>& repeatedStringVector =
194                         *repeatedStringValue;
195                 const char* cStringArray[repeatedStringVector.size()];
196 
197                 for (int i = 0; i < repeatedStringVector.size(); ++i) {
198                     cStringArray[i] = repeatedStringVector[i].has_value()
199                                               ? repeatedStringVector[i]->c_str()
200                                               : "";
201                 }
202 
203                 AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size());
204                 break;
205             }
206             case VendorAtomValue::repeatedBoolValue: {
207                 const std::optional<std::vector<bool>>& repeatedBoolValue =
208                         atomValue.get<VendorAtomValue::repeatedBoolValue>();
209                 if (!repeatedBoolValue) {
210                     AStatsEvent_writeBoolArray(event, {}, 0);
211                     break;
212                 }
213                 const std::vector<bool>& repeatedBoolVector = *repeatedBoolValue;
214                 bool boolArray[repeatedBoolValue->size()];
215 
216                 for (int i = 0; i < repeatedBoolVector.size(); ++i) {
217                     boolArray[i] = repeatedBoolVector[i];
218                 }
219 
220                 AStatsEvent_writeBoolArray(event, boolArray, repeatedBoolVector.size());
221                 break;
222             }
223             case VendorAtomValue::byteArrayValue: {
224                 const std::optional<std::vector<uint8_t>>& byteArrayValue =
225                         atomValue.get<VendorAtomValue::byteArrayValue>();
226                 if (!byteArrayValue) {
227                     AStatsEvent_writeByteArray(event, {}, 0);
228                     break;
229                 }
230                 AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size());
231                 break;
232             }
233             default: {
234                 AStatsEvent_release(event);
235                 ALOGE("Atom ID %ld has invalid atomValue.getTag", (long)vendorAtom.atomId);
236                 Counter::logIncrement(g_AtomErrorMetricName);
237                 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
238                         -1, "invalid atomValue.getTag");
239                 break;
240             }
241         }
242 
243         const auto& valueAnnotationIndex = fieldIndexToAnnotationSetMap.find(atomValueIdx);
244         if (valueAnnotationIndex != fieldIndexToAnnotationSetMap.end()) {
245             const std::vector<Annotation>& fieldAnnotations =
246                     (*vendorAtom.valuesAnnotations)[valueAnnotationIndex->second]->annotations;
247             VLOG("Atom ID %ld has %ld annotations for field #%ld", (long)vendorAtom.atomId,
248                  (long)fieldAnnotations.size(), (long)atomValueIdx + 2);
249             if (!write_field_annotations(event, fieldAnnotations)) {
250                 AStatsEvent_release(event);
251                 ALOGE("Atom ID %ld has incompatible field level annotation for field #%ld",
252                       (long)vendorAtom.atomId, (long)atomValueIdx + 2);
253                 Counter::logIncrement(g_AtomErrorMetricName);
254                 return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
255                         -1, "invalid atom field annotation");
256             }
257         }
258         atomValueIdx++;
259     }
260     AStatsEvent_build(event);
261     const int ret = AStatsEvent_write(event);
262     AStatsEvent_release(event);
263     if (ret <= 0) {
264         ALOGE("Error writing Atom ID %ld. Result: %d", (long)vendorAtom.atomId, ret);
265         Counter::logIncrement(g_AtomErrorMetricName);
266     }
267     return ret <= 0 ? ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret,
268                                                                               "report atom failed")
269                     : ndk::ScopedAStatus::ok();
270 }
271 
272 }  // namespace stats
273 }  // namespace frameworks
274 }  // namespace android
275 }  // namespace aidl
276