Merge "StatsEventCompat"
This commit is contained in:
commit
c9b826c66b
7 changed files with 442 additions and 8 deletions
54
libstats/push_compat/Android.bp
Normal file
54
libstats/push_compat/Android.bp
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
// =========================================================================
|
||||
// Native library that toggles between the old and new statsd socket
|
||||
// protocols. This library should only be used by DNS resolver or other
|
||||
// native modules on Q that log pushed atoms to statsd.
|
||||
// =========================================================================
|
||||
cc_defaults {
|
||||
name: "libstatspush_compat_defaults",
|
||||
srcs: ["StatsEventCompat.cpp"],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
header_libs: ["libstatssocket_headers"],
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libstatssocket_q",
|
||||
"libutils"
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libstatspush_compat",
|
||||
defaults: ["libstatspush_compat_defaults"],
|
||||
export_include_dirs: ["include"],
|
||||
static_libs: ["libgtest_prod"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libstatspush_compat_test",
|
||||
defaults: ["libstatspush_compat_defaults"],
|
||||
test_suites: ["device_tests"],
|
||||
srcs: [
|
||||
"tests/StatsEventCompat_test.cpp",
|
||||
],
|
||||
static_libs: ["libgmock"],
|
||||
}
|
||||
|
221
libstats/push_compat/StatsEventCompat.cpp
Normal file
221
libstats/push_compat/StatsEventCompat.cpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
71
libstats/push_compat/include/StatsEventCompat.h
Normal file
71
libstats/push_compat/include/StatsEventCompat.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtest/gtest_prod.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "stats_event.h"
|
||||
#include "stats_event_list.h"
|
||||
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
class StatsEventCompat {
|
||||
public:
|
||||
StatsEventCompat();
|
||||
~StatsEventCompat();
|
||||
|
||||
void setAtomId(int32_t atomId);
|
||||
void writeInt32(int32_t value);
|
||||
void writeInt64(int64_t value);
|
||||
void writeFloat(float value);
|
||||
void writeBool(bool value);
|
||||
void writeByteArray(const char* buffer, size_t length);
|
||||
void writeString(const char* value);
|
||||
|
||||
// Pre-condition: numUids == tags.size()
|
||||
void writeAttributionChain(const int32_t* uids, size_t numUids,
|
||||
const vector<const char*>& tags);
|
||||
|
||||
void writeKeyValuePairs(const map<int, int32_t>& int32Map, const map<int, int64_t>& int64Map,
|
||||
const map<int, const char*>& stringMap,
|
||||
const map<int, float>& floatMap);
|
||||
|
||||
void addBoolAnnotation(uint8_t annotationId, bool value);
|
||||
void addInt32Annotation(uint8_t annotationId, int32_t value);
|
||||
|
||||
int writeToSocket();
|
||||
|
||||
private:
|
||||
// static member variables
|
||||
const static bool mPlatformAtLeastR;
|
||||
static bool mAttemptedLoad;
|
||||
static std::mutex mLoadLock;
|
||||
static struct stats_event_api_table* mStatsEventApi;
|
||||
|
||||
// non-static member variables
|
||||
struct stats_event* mEventR = nullptr;
|
||||
stats_event_list mEventQ;
|
||||
|
||||
template <class T>
|
||||
void writeKeyValuePairMap(const map<int, T>& keyValuePairMap);
|
||||
|
||||
bool usesNewSchema();
|
||||
FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading);
|
||||
};
|
38
libstats/push_compat/tests/StatsEventCompat_test.cpp
Normal file
38
libstats/push_compat/tests/StatsEventCompat_test.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 <gtest/gtest.h>
|
||||
|
||||
using android::base::GetProperty;
|
||||
|
||||
/* 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 static bool mPlatformAtLeastR = GetProperty("ro.build.version.release", "") == "R" ||
|
||||
android_get_device_api_level() > __ANDROID_API_Q__;
|
||||
|
||||
TEST(StatsEventCompatTest, TestDynamicLoading) {
|
||||
StatsEventCompat event;
|
||||
EXPECT_EQ(mPlatformAtLeastR, event.usesNewSchema());
|
||||
}
|
|
@ -41,3 +41,9 @@ cc_library {
|
|||
"liblog",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_headers {
|
||||
name: "libstatssocket_headers",
|
||||
export_include_dirs: ["include"],
|
||||
host_supported: true,
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ struct stats_event* stats_event_obtain();
|
|||
// The build function can be called multiple times without error. If the event
|
||||
// has been built before, this function is a no-op.
|
||||
void stats_event_build(struct stats_event* event);
|
||||
void stats_event_write(struct stats_event* event);
|
||||
int stats_event_write(struct stats_event* event);
|
||||
void stats_event_release(struct stats_event* event);
|
||||
|
||||
void stats_event_set_atom_id(struct stats_event* event, uint32_t atomId);
|
||||
|
@ -98,7 +98,7 @@ void stats_event_write_bool(struct stats_event* event, bool value);
|
|||
void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf, size_t numBytes);
|
||||
|
||||
// Buf must be null-terminated.
|
||||
void stats_event_write_string8(struct stats_event* event, const char* buf);
|
||||
void stats_event_write_string8(struct stats_event* event, const char* value);
|
||||
|
||||
// Tags must be null-terminated.
|
||||
void stats_event_write_attribution_chain(struct stats_event* event, const uint32_t* uids,
|
||||
|
@ -127,9 +127,33 @@ void stats_event_add_int32_annotation(struct stats_event* event, uint8_t annotat
|
|||
int32_t value);
|
||||
|
||||
uint32_t stats_event_get_atom_id(struct stats_event* event);
|
||||
// Size is an output parameter.
|
||||
uint8_t* stats_event_get_buffer(struct stats_event* event, size_t* size);
|
||||
uint32_t stats_event_get_errors(struct stats_event* event);
|
||||
|
||||
// This table is used by StatsEventCompat to access the stats_event API.
|
||||
struct stats_event_api_table {
|
||||
struct stats_event* (*obtain)(void);
|
||||
void (*build)(struct stats_event*);
|
||||
int (*write)(struct stats_event*);
|
||||
void (*release)(struct stats_event*);
|
||||
void (*set_atom_id)(struct stats_event*, uint32_t);
|
||||
void (*write_int32)(struct stats_event*, int32_t);
|
||||
void (*write_int64)(struct stats_event*, int64_t);
|
||||
void (*write_float)(struct stats_event*, float);
|
||||
void (*write_bool)(struct stats_event*, bool);
|
||||
void (*write_byte_array)(struct stats_event*, const uint8_t*, size_t);
|
||||
void (*write_string8)(struct stats_event*, const char*);
|
||||
void (*write_attribution_chain)(struct stats_event*, const uint32_t*, const char* const*,
|
||||
uint8_t);
|
||||
void (*write_key_value_pairs)(struct stats_event*, struct key_value_pair*, uint8_t);
|
||||
void (*add_bool_annotation)(struct stats_event*, uint8_t, bool);
|
||||
void (*add_int32_annotation)(struct stats_event*, uint8_t, int32_t);
|
||||
uint32_t (*get_atom_id)(struct stats_event*);
|
||||
uint8_t* (*get_buffer)(struct stats_event*, size_t*);
|
||||
uint32_t (*get_errors)(struct stats_event*);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __CPLUSPLUS
|
||||
|
|
|
@ -193,12 +193,12 @@ void stats_event_write_byte_array(struct stats_event* event, const uint8_t* buf,
|
|||
append_byte_array(event, buf, numBytes);
|
||||
}
|
||||
|
||||
// Buf is assumed to be encoded using UTF8
|
||||
void stats_event_write_string8(struct stats_event* event, const char* buf) {
|
||||
// Value is assumed to be encoded using UTF8
|
||||
void stats_event_write_string8(struct stats_event* event, const char* value) {
|
||||
if (event->errors) return;
|
||||
|
||||
start_field(event, STRING_TYPE);
|
||||
append_string(event, buf);
|
||||
append_string(event, value);
|
||||
}
|
||||
|
||||
// Tags are assumed to be encoded using UTF8
|
||||
|
@ -320,8 +320,28 @@ void stats_event_build(struct stats_event* event) {
|
|||
event->built = true;
|
||||
}
|
||||
|
||||
void stats_event_write(struct stats_event* event) {
|
||||
int stats_event_write(struct stats_event* event) {
|
||||
stats_event_build(event);
|
||||
|
||||
write_buffer_to_statsd(&event->buf, event->size, event->atomId);
|
||||
return write_buffer_to_statsd(&event->buf, event->size, event->atomId);
|
||||
}
|
||||
|
||||
struct stats_event_api_table table = {
|
||||
stats_event_obtain,
|
||||
stats_event_build,
|
||||
stats_event_write,
|
||||
stats_event_release,
|
||||
stats_event_set_atom_id,
|
||||
stats_event_write_int32,
|
||||
stats_event_write_int64,
|
||||
stats_event_write_float,
|
||||
stats_event_write_bool,
|
||||
stats_event_write_byte_array,
|
||||
stats_event_write_string8,
|
||||
stats_event_write_attribution_chain,
|
||||
stats_event_write_key_value_pairs,
|
||||
stats_event_add_bool_annotation,
|
||||
stats_event_add_int32_annotation,
|
||||
stats_event_get_atom_id,
|
||||
stats_event_get_buffer,
|
||||
stats_event_get_errors,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue