Also fixes a bug in the tests where the platform version was being
checked by querying version.release instead of version.codename
(currently, version.release == 10, while version.codename == R).
Test: bit libstatspush_compat_test:* (on Q and R)
Test: libsstatssocket.so is not placed in resolv apex (on R)
1|bonito:/ $ ls apex/com.android.resolv/lib64
libclang_rt.ubsan_standalone-aarch64-android.so
libcrypto.so
libnetd_resolv.so
libssl.so
Test: NETWORK_DNS_EVENT_REPORTED atom is logged to statsd (on R)
- adb shell cmd stats print-stats (atom 116 count > 0)
Bug: 148743333
Change-Id: Ib3eaa32835905bcf6e3b003054bf0f3c4e7ec0a4
Merged-In: Ib3eaa32835905bcf6e3b003054bf0f3c4e7ec0a4
(cherry picked from commit 9848eb4f1c)
253 lines
8.7 KiB
C++
253 lines
8.7 KiB
C++
/*
|
|
* Copyright (C) 2019 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.
|
|
*/
|
|
|
|
#include "include/StatsEventCompat.h"
|
|
|
|
#include <chrono>
|
|
|
|
#include <android-base/chrono_utils.h>
|
|
#include <android-base/properties.h>
|
|
#include <android/api-level.h>
|
|
#include <android/log.h>
|
|
#include <dlfcn.h>
|
|
|
|
using android::base::boot_clock;
|
|
using android::base::GetProperty;
|
|
|
|
const static int kStatsEventTag = 1937006964;
|
|
|
|
/* Checking ro.build.version.release is fragile, as the release field is
|
|
* an opaque string without structural guarantees. However, testing confirms
|
|
* that on Q devices, the property is "10," and on R, it is "R." Until
|
|
* android_get_device_api_level() is updated, this is the only solution.
|
|
*
|
|
* TODO(b/146019024): migrate to android_get_device_api_level()
|
|
*/
|
|
const bool StatsEventCompat::mPlatformAtLeastR =
|
|
GetProperty("ro.build.version.codename", "") == "R" ||
|
|
android_get_device_api_level() > __ANDROID_API_Q__;
|
|
|
|
// initializations of static class variables
|
|
bool StatsEventCompat::mAttemptedLoad = false;
|
|
std::mutex StatsEventCompat::mLoadLock;
|
|
AStatsEventApi StatsEventCompat::mAStatsEventApi;
|
|
|
|
static int64_t elapsedRealtimeNano() {
|
|
return std::chrono::time_point_cast<std::chrono::nanoseconds>(boot_clock::now())
|
|
.time_since_epoch()
|
|
.count();
|
|
}
|
|
|
|
StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
|
|
// guard loading because StatsEventCompat might be called from multithreaded
|
|
// environment
|
|
{
|
|
std::lock_guard<std::mutex> lg(mLoadLock);
|
|
if (!mAttemptedLoad && mPlatformAtLeastR) {
|
|
void* handle = dlopen("libstatssocket.so", RTLD_NOW);
|
|
if (handle) {
|
|
initializeApiTableLocked(handle);
|
|
} else {
|
|
ALOGE("dlopen failed: %s\n", dlerror());
|
|
}
|
|
}
|
|
mAttemptedLoad = true;
|
|
}
|
|
|
|
if (useRSchema()) {
|
|
mEventR = mAStatsEventApi.obtain();
|
|
} else if (useQSchema()) {
|
|
mEventQ << elapsedRealtimeNano();
|
|
}
|
|
}
|
|
|
|
StatsEventCompat::~StatsEventCompat() {
|
|
if (useRSchema()) mAStatsEventApi.release(mEventR);
|
|
}
|
|
|
|
// Populates the AStatsEventApi struct by calling dlsym to find the address of
|
|
// each API function.
|
|
void StatsEventCompat::initializeApiTableLocked(void* handle) {
|
|
mAStatsEventApi.obtain = (AStatsEvent* (*)())dlsym(handle, "AStatsEvent_obtain");
|
|
mAStatsEventApi.build = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_build");
|
|
mAStatsEventApi.write = (int (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_write");
|
|
mAStatsEventApi.release = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_release");
|
|
mAStatsEventApi.setAtomId =
|
|
(void (*)(AStatsEvent*, uint32_t))dlsym(handle, "AStatsEvent_setAtomId");
|
|
mAStatsEventApi.writeInt32 =
|
|
(void (*)(AStatsEvent*, int32_t))dlsym(handle, "AStatsEvent_writeInt32");
|
|
mAStatsEventApi.writeInt64 =
|
|
(void (*)(AStatsEvent*, int64_t))dlsym(handle, "AStatsEvent_writeInt64");
|
|
mAStatsEventApi.writeFloat =
|
|
(void (*)(AStatsEvent*, float))dlsym(handle, "AStatsEvent_writeFloat");
|
|
mAStatsEventApi.writeBool =
|
|
(void (*)(AStatsEvent*, bool))dlsym(handle, "AStatsEvent_writeBool");
|
|
mAStatsEventApi.writeByteArray = (void (*)(AStatsEvent*, const uint8_t*, size_t))dlsym(
|
|
handle, "AStatsEvent_writeByteArray");
|
|
mAStatsEventApi.writeString =
|
|
(void (*)(AStatsEvent*, const char*))dlsym(handle, "AStatsEvent_writeString");
|
|
mAStatsEventApi.writeAttributionChain =
|
|
(void (*)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t))dlsym(
|
|
handle, "AStatsEvent_writeAttributionChain");
|
|
mAStatsEventApi.addBoolAnnotation =
|
|
(void (*)(AStatsEvent*, uint8_t, bool))dlsym(handle, "AStatsEvent_addBoolAnnotation");
|
|
mAStatsEventApi.addInt32Annotation = (void (*)(AStatsEvent*, uint8_t, int32_t))dlsym(
|
|
handle, "AStatsEvent_addInt32Annotation");
|
|
|
|
mAStatsEventApi.initialized = true;
|
|
}
|
|
|
|
void StatsEventCompat::setAtomId(int32_t atomId) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.setAtomId(mEventR, (uint32_t)atomId);
|
|
} else if (useQSchema()) {
|
|
mEventQ << atomId;
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeInt32(int32_t value) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.writeInt32(mEventR, value);
|
|
} else if (useQSchema()) {
|
|
mEventQ << value;
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeInt64(int64_t value) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.writeInt64(mEventR, value);
|
|
} else if (useQSchema()) {
|
|
mEventQ << value;
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeFloat(float value) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.writeFloat(mEventR, value);
|
|
} else if (useQSchema()) {
|
|
mEventQ << value;
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeBool(bool value) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.writeBool(mEventR, value);
|
|
} else if (useQSchema()) {
|
|
mEventQ << value;
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.writeByteArray(mEventR, reinterpret_cast<const uint8_t*>(buffer), length);
|
|
} else if (useQSchema()) {
|
|
mEventQ.AppendCharArray(buffer, length);
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeString(const char* value) {
|
|
if (value == nullptr) value = "";
|
|
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.writeString(mEventR, value);
|
|
} else if (useQSchema()) {
|
|
mEventQ << value;
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
|
|
const vector<const char*>& tags) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),
|
|
(uint8_t)numUids);
|
|
} else if (useQSchema()) {
|
|
mEventQ.begin();
|
|
for (size_t i = 0; i < numUids; i++) {
|
|
mEventQ.begin();
|
|
mEventQ << uids[i];
|
|
const char* tag = tags[i] ? tags[i] : "";
|
|
mEventQ << tag;
|
|
mEventQ.end();
|
|
}
|
|
mEventQ.end();
|
|
}
|
|
}
|
|
|
|
void StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map,
|
|
const map<int, int64_t>& int64Map,
|
|
const map<int, const char*>& stringMap,
|
|
const map<int, float>& floatMap) {
|
|
// AStatsEvent does not support key value pairs.
|
|
if (useQSchema()) {
|
|
mEventQ.begin();
|
|
writeKeyValuePairMap(int32Map);
|
|
writeKeyValuePairMap(int64Map);
|
|
writeKeyValuePairMap(stringMap);
|
|
writeKeyValuePairMap(floatMap);
|
|
mEventQ.end();
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void StatsEventCompat::writeKeyValuePairMap(const map<int, T>& keyValuePairMap) {
|
|
for (const auto& it : keyValuePairMap) {
|
|
mEventQ.begin();
|
|
mEventQ << it.first;
|
|
mEventQ << it.second;
|
|
mEventQ.end();
|
|
}
|
|
}
|
|
|
|
// explicitly specify which types we're going to use
|
|
template void StatsEventCompat::writeKeyValuePairMap<int32_t>(const map<int, int32_t>&);
|
|
template void StatsEventCompat::writeKeyValuePairMap<int64_t>(const map<int, int64_t>&);
|
|
template void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float>&);
|
|
template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);
|
|
|
|
void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.addBoolAnnotation(mEventR, annotationId, value);
|
|
}
|
|
// Don't do anything if on Q.
|
|
}
|
|
|
|
void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.addInt32Annotation(mEventR, annotationId, value);
|
|
}
|
|
// Don't do anything if on Q.
|
|
}
|
|
|
|
int StatsEventCompat::writeToSocket() {
|
|
if (useRSchema()) {
|
|
mAStatsEventApi.build(mEventR);
|
|
return mAStatsEventApi.write(mEventR);
|
|
}
|
|
|
|
if (useQSchema()) return mEventQ.write(LOG_ID_STATS);
|
|
|
|
// We reach here only if we're on R, but libstatssocket was unable to
|
|
// be loaded using dlopen.
|
|
return -ENOLINK;
|
|
}
|
|
|
|
bool StatsEventCompat::useRSchema() {
|
|
return mPlatformAtLeastR && mAStatsEventApi.initialized;
|
|
}
|
|
|
|
bool StatsEventCompat::useQSchema() {
|
|
return !mPlatformAtLeastR;
|
|
}
|