| /* |
| * 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; |
| } |