/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define DEBUG false // STOPSHIP if true #define LOG_TAG "StatsAidl" #define VLOG(...) \ if (DEBUG) ALOGD(__VA_ARGS__); #include "StatsAidl.h" #include #include #include #include #include #include namespace { static const char* g_AtomErrorMetricName = "statsd_errors.value_report_vendor_atom_errors_count"; } namespace aidl { namespace android { namespace frameworks { namespace stats { using ::android::expresslog::Counter; template constexpr typename std::underlying_type::type to_underlying(E e) noexcept { return static_cast::type>(e); } StatsHal::StatsHal() { } bool write_annotation(AStatsEvent* event, const Annotation& annotation) { switch (annotation.value.getTag()) { case AnnotationValue::boolValue: { AStatsEvent_addBoolAnnotation(event, to_underlying(annotation.annotationId), annotation.value.get()); break; } case AnnotationValue::intValue: { AStatsEvent_addInt32Annotation(event, to_underlying(annotation.annotationId), annotation.value.get()); break; } default: { return false; } } return true; } bool write_atom_annotations(AStatsEvent* event, const std::vector>& annotations) { for (const auto& atomAnnotation : annotations) { if (!atomAnnotation) { return false; } if (!write_annotation(event, *atomAnnotation)) { return false; } } return true; } bool write_field_annotations(AStatsEvent* event, const std::vector& annotations) { for (const auto& fieldAnnotation : annotations) { if (!write_annotation(event, fieldAnnotation)) { return false; } } return true; } ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) { ALOGE("Atom ID %ld is not a valid vendor atom ID", (long)vendorAtom.atomId); Counter::logIncrement(g_AtomErrorMetricName); return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( -1, "Not a valid vendor atom ID"); } if (vendorAtom.reverseDomainName.length() > 50) { ALOGE("Vendor atom reverse domain name %s is too long.", vendorAtom.reverseDomainName.c_str()); Counter::logIncrement(g_AtomErrorMetricName); return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( -1, "Vendor atom reverse domain name is too long"); } AStatsEvent* event = AStatsEvent_obtain(); AStatsEvent_setAtomId(event, vendorAtom.atomId); if (vendorAtom.atomAnnotations) { if (!write_atom_annotations(event, *vendorAtom.atomAnnotations)) { AStatsEvent_release(event); ALOGE("Atom ID %ld has incompatible atom level annotation", (long)vendorAtom.atomId); Counter::logIncrement(g_AtomErrorMetricName); return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( -1, "invalid atom annotation"); } } // populate map for quickier access for VendorAtomValue associated annotations by value index std::unordered_map fieldIndexToAnnotationSetMap; if (vendorAtom.valuesAnnotations) { const std::vector>& valuesAnnotations = *vendorAtom.valuesAnnotations; for (int i = 0; i < valuesAnnotations.size(); i++) { if (valuesAnnotations[i]) { fieldIndexToAnnotationSetMap[valuesAnnotations[i]->valueIndex] = i; } } } AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str()); size_t atomValueIdx = 0; for (const auto& atomValue : vendorAtom.values) { switch (atomValue.getTag()) { case VendorAtomValue::intValue: AStatsEvent_writeInt32(event, atomValue.get()); break; case VendorAtomValue::longValue: AStatsEvent_writeInt64(event, atomValue.get()); break; case VendorAtomValue::floatValue: AStatsEvent_writeFloat(event, atomValue.get()); break; case VendorAtomValue::stringValue: AStatsEvent_writeString(event, atomValue.get().c_str()); break; case VendorAtomValue::boolValue: AStatsEvent_writeBool(event, atomValue.get()); break; case VendorAtomValue::repeatedIntValue: { const std::optional>& repeatedIntValue = atomValue.get(); if (!repeatedIntValue) { AStatsEvent_writeInt32Array(event, {}, 0); break; } AStatsEvent_writeInt32Array(event, repeatedIntValue->data(), repeatedIntValue->size()); break; } case VendorAtomValue::repeatedLongValue: { const std::optional>& repeatedLongValue = atomValue.get(); if (!repeatedLongValue) { AStatsEvent_writeInt64Array(event, {}, 0); break; } AStatsEvent_writeInt64Array(event, repeatedLongValue->data(), repeatedLongValue->size()); break; } case VendorAtomValue::repeatedFloatValue: { const std::optional>& repeatedFloatValue = atomValue.get(); if (!repeatedFloatValue) { AStatsEvent_writeFloatArray(event, {}, 0); break; } AStatsEvent_writeFloatArray(event, repeatedFloatValue->data(), repeatedFloatValue->size()); break; } case VendorAtomValue::repeatedStringValue: { const std::optional>>& repeatedStringValue = atomValue.get(); if (!repeatedStringValue) { AStatsEvent_writeStringArray(event, {}, 0); break; } const std::vector>& repeatedStringVector = *repeatedStringValue; const char* cStringArray[repeatedStringVector.size()]; for (int i = 0; i < repeatedStringVector.size(); ++i) { cStringArray[i] = repeatedStringVector[i].has_value() ? repeatedStringVector[i]->c_str() : ""; } AStatsEvent_writeStringArray(event, cStringArray, repeatedStringVector.size()); break; } case VendorAtomValue::repeatedBoolValue: { const std::optional>& repeatedBoolValue = atomValue.get(); if (!repeatedBoolValue) { AStatsEvent_writeBoolArray(event, {}, 0); break; } const std::vector& repeatedBoolVector = *repeatedBoolValue; bool boolArray[repeatedBoolValue->size()]; for (int i = 0; i < repeatedBoolVector.size(); ++i) { boolArray[i] = repeatedBoolVector[i]; } AStatsEvent_writeBoolArray(event, boolArray, repeatedBoolVector.size()); break; } case VendorAtomValue::byteArrayValue: { const std::optional>& byteArrayValue = atomValue.get(); if (!byteArrayValue) { AStatsEvent_writeByteArray(event, {}, 0); break; } AStatsEvent_writeByteArray(event, byteArrayValue->data(), byteArrayValue->size()); break; } default: { AStatsEvent_release(event); ALOGE("Atom ID %ld has invalid atomValue.getTag", (long)vendorAtom.atomId); Counter::logIncrement(g_AtomErrorMetricName); return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( -1, "invalid atomValue.getTag"); break; } } const auto& valueAnnotationIndex = fieldIndexToAnnotationSetMap.find(atomValueIdx); if (valueAnnotationIndex != fieldIndexToAnnotationSetMap.end()) { const std::vector& fieldAnnotations = (*vendorAtom.valuesAnnotations)[valueAnnotationIndex->second]->annotations; VLOG("Atom ID %ld has %ld annotations for field #%ld", (long)vendorAtom.atomId, (long)fieldAnnotations.size(), (long)atomValueIdx + 2); if (!write_field_annotations(event, fieldAnnotations)) { AStatsEvent_release(event); ALOGE("Atom ID %ld has incompatible field level annotation for field #%ld", (long)vendorAtom.atomId, (long)atomValueIdx + 2); Counter::logIncrement(g_AtomErrorMetricName); return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( -1, "invalid atom field annotation"); } } atomValueIdx++; } AStatsEvent_build(event); const int ret = AStatsEvent_write(event); AStatsEvent_release(event); if (ret <= 0) { ALOGE("Error writing Atom ID %ld. Result: %d", (long)vendorAtom.atomId, ret); Counter::logIncrement(g_AtomErrorMetricName); } return ret <= 0 ? ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret, "report atom failed") : ndk::ScopedAStatus::ok(); } } // namespace stats } // namespace frameworks } // namespace android } // namespace aidl