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