| /* | |
| * 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; | |
| const bool StatsEventCompat::mPlatformAtLeastR = | |
| android_get_device_api_level() >= __ANDROID_API_R__; | |
| // 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()) { | |
| 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; | |
| } |