blob: edfa070ed43715972206dda1f2838ad3e58e53d1 [file] [log] [blame]
/*
* 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 <android-base/properties.h>
#include <android/api-level.h>
#include <android/log.h>
#include <dlfcn.h>
#include <utils/SystemClock.h>
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__;
// definitions of static class variables
bool StatsEventCompat::mAttemptedLoad = false;
struct stats_event_api_table* StatsEventCompat::mStatsEventApi = nullptr;
std::mutex StatsEventCompat::mLoadLock;
StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {
// guard loading because StatsEventCompat might be called from multithreaded
// environment
{
std::lock_guard<std::mutex> lg(mLoadLock);
if (!mAttemptedLoad) {
void* handle = dlopen("libstatssocket.so", RTLD_NOW);
if (handle) {
mStatsEventApi = (struct stats_event_api_table*)dlsym(handle, "table");
} else {
ALOGE("dlopen failed: %s\n", dlerror());
}
}
mAttemptedLoad = true;
}
if (mStatsEventApi) {
mEventR = mStatsEventApi->obtain();
} else if (!mPlatformAtLeastR) {
mEventQ << android::elapsedRealtimeNano();
}
}
StatsEventCompat::~StatsEventCompat() {
if (mStatsEventApi) mStatsEventApi->release(mEventR);
}
void StatsEventCompat::setAtomId(int32_t atomId) {
if (mStatsEventApi) {
mStatsEventApi->set_atom_id(mEventR, (uint32_t)atomId);
} else if (!mPlatformAtLeastR) {
mEventQ << atomId;
}
}
void StatsEventCompat::writeInt32(int32_t value) {
if (mStatsEventApi) {
mStatsEventApi->write_int32(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
}
void StatsEventCompat::writeInt64(int64_t value) {
if (mStatsEventApi) {
mStatsEventApi->write_int64(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
}
void StatsEventCompat::writeFloat(float value) {
if (mStatsEventApi) {
mStatsEventApi->write_float(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
}
void StatsEventCompat::writeBool(bool value) {
if (mStatsEventApi) {
mStatsEventApi->write_bool(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
}
void StatsEventCompat::writeByteArray(const char* buffer, size_t length) {
if (mStatsEventApi) {
mStatsEventApi->write_byte_array(mEventR, (const uint8_t*)buffer, length);
} else if (!mPlatformAtLeastR) {
mEventQ.AppendCharArray(buffer, length);
}
}
void StatsEventCompat::writeString(const char* value) {
if (value == nullptr) value = "";
if (mStatsEventApi) {
mStatsEventApi->write_string8(mEventR, value);
} else if (!mPlatformAtLeastR) {
mEventQ << value;
}
}
void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,
const vector<const char*>& tags) {
if (mStatsEventApi) {
mStatsEventApi->write_attribution_chain(mEventR, (const uint32_t*)uids, tags.data(),
(uint8_t)numUids);
} else if (!mPlatformAtLeastR) {
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) {
if (mStatsEventApi) {
vector<struct key_value_pair> pairs;
for (const auto& it : int32Map) {
pairs.push_back({.key = it.first, .valueType = INT32_TYPE, .int32Value = it.second});
}
for (const auto& it : int64Map) {
pairs.push_back({.key = it.first, .valueType = INT64_TYPE, .int64Value = it.second});
}
for (const auto& it : stringMap) {
pairs.push_back({.key = it.first, .valueType = STRING_TYPE, .stringValue = it.second});
}
for (const auto& it : floatMap) {
pairs.push_back({.key = it.first, .valueType = FLOAT_TYPE, .floatValue = it.second});
}
mStatsEventApi->write_key_value_pairs(mEventR, pairs.data(), (uint8_t)pairs.size());
}
else if (!mPlatformAtLeastR) {
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 (mStatsEventApi) mStatsEventApi->add_bool_annotation(mEventR, annotationId, value);
// Don't do anything if on Q.
}
void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {
if (mStatsEventApi) mStatsEventApi->add_int32_annotation(mEventR, annotationId, value);
// Don't do anything if on Q.
}
int StatsEventCompat::writeToSocket() {
if (mStatsEventApi) {
mStatsEventApi->build(mEventR);
return mStatsEventApi->write(mEventR);
}
if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS);
// We reach here only if we're on R, but libstatspush_compat was unable to
// be loaded using dlopen.
return -ENOLINK;
}
bool StatsEventCompat::usesNewSchema() {
return mStatsEventApi != nullptr;
}