diff --git a/libstats/pull_lazy/Android.bp b/libstats/pull_lazy/Android.bp new file mode 100644 index 000000000..b1d098b9a --- /dev/null +++ b/libstats/pull_lazy/Android.bp @@ -0,0 +1,44 @@ +// Lazy loading version of libstatspull that can be used by code +// that is running before the statsd APEX is mounted and +// libstatspull.so is available. +cc_library_static { + name: "libstatspull_lazy", + header_libs: [ + "libstatspull_headers", + "libstatssocket_headers", + ], + export_header_lib_headers: [ + "libstatspull_headers", + ], + apex_available: ["//apex_available:platform"], + srcs: ["libstatspull_lazy.cpp"], +} + +cc_test { + name: "libstatspull_lazy_test", + srcs: [ + "tests/libstatspull_lazy_test.cpp", + ], + static_libs: [ + "libstatspull_lazy", + "libstatssocket_lazy", + ], + shared_libs: ["liblog"], + cflags: [ + "-Wall", + "-Werror", + ], + test_suites: ["device-tests", "mts-statsd"], + test_config: "libstatspull_lazy_test.xml", + // TODO(b/153588990): Remove when the build system properly separates. + // 32bit and 64bit architectures. + compile_multilib: "both", + multilib: { + lib64: { + suffix: "64", + }, + lib32: { + suffix: "32", + }, + }, +} \ No newline at end of file diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING new file mode 100644 index 000000000..89b8c2a1f --- /dev/null +++ b/libstats/pull_lazy/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit" : [ + { + "name" : "libstatspull_lazy_test" + } + ] +} \ No newline at end of file diff --git a/libstats/pull_lazy/libstatspull_lazy.cpp b/libstats/pull_lazy/libstatspull_lazy.cpp new file mode 100644 index 000000000..b11fceec1 --- /dev/null +++ b/libstats/pull_lazy/libstatspull_lazy.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2021 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 "libstatspull_lazy.h" + +#include + +#include +#include + +#include "log/log.h" + +#include "stats_pull_atom_callback.h" + +// This file provides a lazy interface to libstatspull.so to address early boot dependencies. +// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and +// libstatspull.so is in the statsd APEX. + +// Method pointers to libstatspull methods are held in an array which simplifies checking +// all pointers are initialized. +enum MethodIndex { + // PullAtomMetadata APIs in stats_pull_atom_callback.h. + k_AStatsManager_PullAtomMetadata_obtain, + k_AStatsManager_PullAtomMetadata_release, + k_AStatsManager_PullAtomMetadata_setCoolDownMillis, + k_AStatsManager_PullAtomMetadata_getCoolDownMillis, + k_AStatsManager_PullAtomMetadata_setTimeoutMillis, + k_AStatsManager_PullAtomMetadata_getTimeoutMillis, + k_AStatsManager_PullAtomMetadata_setAdditiveFields, + k_AStatsManager_PullAtomMetadata_getNumAdditiveFields, + k_AStatsManager_PullAtomMetadata_getAdditiveFields, + + // AStatsEventList APIs in stats_pull_atom_callback.h + k_AStatsEventList_addStatsEvent, + + // PullAtomCallback APIs in stats_pull_atom_callback.h + k_AStatsManager_setPullAtomCallback, + k_AStatsManager_clearPullAtomCallback, + + // Marker for count of methods + k_MethodCount +}; + +// Table of methods pointers in libstatspull APIs. +static void* g_Methods[k_MethodCount]; + +// +// Libstatspull lazy loading. +// + +static atomic_bool gPreventLibstatspullLoading = false; // Allows tests to block loading. + +void PreventLibstatspullLazyLoadingForTests() { + gPreventLibstatspullLoading.store(true); +} + +static void* LoadLibstatspull(int dlopen_flags) { + if (gPreventLibstatspullLoading.load()) { + return nullptr; + } + return dlopen("libstatspull.so", dlopen_flags); +} + +// +// Initialization and symbol binding. + +static void BindSymbol(void* handle, const char* name, enum MethodIndex index) { + void* symbol = dlsym(handle, name); + LOG_ALWAYS_FATAL_IF(symbol == nullptr, "Failed to find symbol '%s' in libstatspull.so: %s", + name, dlerror()); + g_Methods[index] = symbol; +} + +static void InitializeOnce() { + void* handle = LoadLibstatspull(RTLD_NOW); + LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load libstatspull.so: %s", dlerror()); + +#undef BIND_SYMBOL +#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name); + // PullAtomMetadata APIs in stats_pull_atom_callback.h. + BIND_SYMBOL(AStatsManager_PullAtomMetadata_obtain); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_release); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_setCoolDownMillis); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_getCoolDownMillis); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_setTimeoutMillis); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_getTimeoutMillis); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_setAdditiveFields); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_getNumAdditiveFields); + BIND_SYMBOL(AStatsManager_PullAtomMetadata_getAdditiveFields); + + // AStatsEventList APIs in stats_pull_atom_callback.h + BIND_SYMBOL(AStatsEventList_addStatsEvent); + + // PullAtomCallback APIs in stats_pull_atom_callback.h + BIND_SYMBOL(AStatsManager_setPullAtomCallback); + BIND_SYMBOL(AStatsManager_clearPullAtomCallback); + +#undef BIND_SYMBOL + + // Check every symbol is bound. + for (int i = 0; i < k_MethodCount; ++i) { + LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr, + "Uninitialized method in libstatspull_lazy at index: %d", i); + } +} + +static void EnsureInitialized() { + static std::once_flag initialize_flag; + std::call_once(initialize_flag, InitializeOnce); +} + +#define INVOKE_METHOD(name, args...) \ + do { \ + EnsureInitialized(); \ + void* method = g_Methods[k_##name]; \ + return reinterpret_cast(method)(args); \ + } while (0) + +// +// Forwarding for methods in stats_pull_atom_callback.h. +// + +AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_obtain); +} + +void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_release, metadata); +} + +void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata, + int64_t cool_down_millis) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_setCoolDownMillis, metadata, cool_down_millis); +} + +int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_getCoolDownMillis, metadata); +} + +void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata, + int64_t timeout_millis) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_setTimeoutMillis, metadata, timeout_millis); +} + +int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_getTimeoutMillis, metadata); +} + +void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata, + int32_t* additive_fields, + int32_t num_fields) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_setAdditiveFields, metadata, additive_fields, + num_fields); +} + +int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields( + AStatsManager_PullAtomMetadata* metadata) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_getNumAdditiveFields, metadata); +} + +void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata, + int32_t* fields) { + INVOKE_METHOD(AStatsManager_PullAtomMetadata_getAdditiveFields, metadata, fields); +} + +AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) { + INVOKE_METHOD(AStatsEventList_addStatsEvent, pull_data); +} + +void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata, + AStatsManager_PullAtomCallback callback, void* cookie) { + INVOKE_METHOD(AStatsManager_setPullAtomCallback, atom_tag, metadata, callback, cookie); +} + +void AStatsManager_clearPullAtomCallback(int32_t atom_tag) { + INVOKE_METHOD(AStatsManager_clearPullAtomCallback, atom_tag); +} diff --git a/libstats/pull_lazy/libstatspull_lazy.h b/libstats/pull_lazy/libstatspull_lazy.h new file mode 100644 index 000000000..2edddc770 --- /dev/null +++ b/libstats/pull_lazy/libstatspull_lazy.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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 + +extern "C" void PreventLibstatspullLazyLoadingForTests(); \ No newline at end of file diff --git a/libstats/pull_lazy/libstatspull_lazy_test.xml b/libstats/pull_lazy/libstatspull_lazy_test.xml new file mode 100644 index 000000000..1b619af1e --- /dev/null +++ b/libstats/pull_lazy/libstatspull_lazy_test.xml @@ -0,0 +1,37 @@ + + + + \ No newline at end of file diff --git a/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp b/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp new file mode 100644 index 000000000..41f82d0b5 --- /dev/null +++ b/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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 "../libstatspull_lazy.h" + +#include + +#include "stats_pull_atom_callback.h" +//#include "stats_event.h" + +// The tests here are just for the case when libstatspull.so cannot be loaded by +// libstatspull_lazy. +class LibstatspullLazyTest : public ::testing::Test { + protected: + virtual void SetUp() { + ::testing::Test::SetUp(); + PreventLibstatspullLazyLoadingForTests(); + } +}; + +static const char* kLoadFailed = "Failed to load libstatspull.so"; + +TEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomMetadata) { + AStatsManager_PullAtomMetadata* metadata = NULL; + EXPECT_DEATH(AStatsManager_PullAtomMetadata_obtain(), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_release(metadata), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 0), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 0), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, NULL, 0), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), kLoadFailed); + EXPECT_DEATH(AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, NULL), kLoadFailed); +} + +TEST_F(LibstatspullLazyTest, NoLibstatspullForAStatsEventList) { + AStatsEventList* event_list = NULL; + EXPECT_DEATH(AStatsEventList_addStatsEvent(event_list), kLoadFailed); +} + +TEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomCallback) { + AStatsManager_PullAtomCallback callback = NULL; + EXPECT_DEATH(AStatsManager_setPullAtomCallback(0, NULL, callback, NULL), kLoadFailed); + EXPECT_DEATH(AStatsManager_clearPullAtomCallback(0), kLoadFailed); +} \ No newline at end of file