/* * 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 #include #include #include #include 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 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& 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& int32Map, const map& int64Map, const map& stringMap, const map& floatMap) { if (mStatsEventApi) { vector 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 void StatsEventCompat::writeKeyValuePairMap(const map& 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(const map&); template void StatsEventCompat::writeKeyValuePairMap(const map&); template void StatsEventCompat::writeKeyValuePairMap(const map&); template void StatsEventCompat::writeKeyValuePairMap(const map&); 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; }