diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp new file mode 100644 index 000000000..9772da164 --- /dev/null +++ b/libstats/pull/Android.bp @@ -0,0 +1,41 @@ +// +// 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 to register a pull atom callback with statsd +// ========================================================== +cc_library_shared { + name: "libstatspull", + srcs: [ + ":statsd_aidl", + "stats_pull_atom_callback.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + ], + export_include_dirs: ["include"], + shared_libs: [ + //TODO: use libbinder_ndk. + "libbinder", + "libstatssocket", + "libservices", + ], + static_libs: [ + "liblog", + "libutils", + ] +} diff --git a/libstats/pull/OWNERS b/libstats/pull/OWNERS new file mode 100644 index 000000000..7855774a7 --- /dev/null +++ b/libstats/pull/OWNERS @@ -0,0 +1,7 @@ +joeo@google.com +muhammadq@google.com +ruchirr@google.com +singhtejinder@google.com +tsaichristine@google.com +yaochen@google.com +yro@google.com diff --git a/libstats/pull/include/stats_pull_atom_callback.h b/libstats/pull/include/stats_pull_atom_callback.h new file mode 100644 index 000000000..ee69ea7a8 --- /dev/null +++ b/libstats/pull/include/stats_pull_atom_callback.h @@ -0,0 +1,46 @@ +/* + * 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Metadata for registering a stats_pull_atom_callback. + * All fields are optional, and defaults will be used for unspecified fields. + */ +typedef struct pull_atom_metadata { + int64_t cool_down_ns; + int64_t timeout_ns; + int32_t* additive_fields; + int32_t additive_fields_size; +} pull_atom_metadata; + +typedef struct pulled_stats_event_list pulled_stats_event_list; + +typedef bool (*stats_pull_atom_callback_t)(int32_t atom_tag, pulled_stats_event_list* data, + const void* cookie); + +struct stats_event* add_stats_event_to_pull_data(pulled_stats_event_list* pull_data); +void register_stats_pull_atom_callback(int32_t atom_tag, stats_pull_atom_callback_t* callback, + pull_atom_metadata* metadata, const void* cookie); + +#ifdef __cplusplus +} +#endif diff --git a/libstats/pull/stats_pull_atom_callback.cpp b/libstats/pull/stats_pull_atom_callback.cpp new file mode 100644 index 000000000..f61d64627 --- /dev/null +++ b/libstats/pull/stats_pull_atom_callback.cpp @@ -0,0 +1,148 @@ +/* + * 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 + +#include +#include + +#include +#include +#include +#include +#include +#include "include/stats_pull_atom_callback.h" + +struct pulled_stats_event_list { + std::vector data; +}; + +struct stats_event* add_stats_event_to_pull_data(pulled_stats_event_list* pull_data) { + struct stats_event* event = stats_event_obtain(); + pull_data->data.push_back(event); + return event; +} + +static const int64_t DEFAULT_COOL_DOWN_NS = 1000000000LL; // 1 second. +static const int64_t DEFAULT_TIMEOUT_NS = 10000000000LL; // 10 seconds. + +class StatsPullAtomCallbackInternal : public android::os::BnPullAtomCallback { + public: + StatsPullAtomCallbackInternal(const stats_pull_atom_callback_t* callback, const void* cookie, + const int64_t coolDownNs, const int64_t timeoutNs, + const std::vector additiveFields) + : mCallback(callback), + mCookie(cookie), + mCoolDownNs(coolDownNs), + mTimeoutNs(timeoutNs), + mAdditiveFields(additiveFields) {} + + ::android::binder::Status onPullAtom( + int32_t atomTag, + const ::android::sp<::android::os::IPullAtomResultReceiver>& resultReceiver) override { + pulled_stats_event_list statsEventList; + bool success = (*mCallback)(atomTag, &statsEventList, mCookie); + std::vector output; + // TODO convert stats_event into parcelable stats_event. + resultReceiver->pullFinished(atomTag, success, output); + for (int i = 0; i < statsEventList.data.size(); i++) { + stats_event_release(statsEventList.data[i]); + } + return android::binder::Status::ok(); + } + + const int64_t& getCoolDownNs() const { return mCoolDownNs; } + const int64_t& getTimeoutNs() const { return mTimeoutNs; } + const std::vector& getAdditiveFields() const { return mAdditiveFields; } + + private: + const stats_pull_atom_callback_t* mCallback; + const void* mCookie; + const int64_t mCoolDownNs; + const int64_t mTimeoutNs; + const std::vector mAdditiveFields; +}; + +static std::mutex pullAtomMutex; +static android::sp sStatsd = nullptr; + +static std::map> mPullers; +static android::sp getStatsServiceLocked(); + +class StatsDeathRecipient : public android::IBinder::DeathRecipient { + public: + StatsDeathRecipient() = default; + ~StatsDeathRecipient() override = default; + + // android::IBinder::DeathRecipient override: + void binderDied(const android::wp& /* who */) override { + std::lock_guard lock(pullAtomMutex); + if (sStatsd) { + sStatsd = nullptr; + } + android::sp statsService = getStatsServiceLocked(); + if (statsService == nullptr) { + return; + } + for (auto it : mPullers) { + statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownNs(), + it.second->getTimeoutNs(), + it.second->getAdditiveFields(), it.second); + } + } +}; + +static android::sp statsDeathRecipient = new StatsDeathRecipient(); + +static android::sp getStatsServiceLocked() { + if (!sStatsd) { + // Fetch statsd. + const android::sp binder = + android::defaultServiceManager()->checkService(android::String16("stats")); + if (!binder) { + return nullptr; + } + binder->linkToDeath(statsDeathRecipient); + sStatsd = android::interface_cast(binder); + } + return sStatsd; +} + +void register_stats_pull_atom_callback(int32_t atom_tag, stats_pull_atom_callback_t* callback, + pull_atom_metadata* metadata, void* cookie) { + int64_t coolDownNs = metadata == nullptr ? DEFAULT_COOL_DOWN_NS : metadata->cool_down_ns; + int64_t timeoutNs = metadata == nullptr ? DEFAULT_TIMEOUT_NS : metadata->timeout_ns; + + std::vector additiveFields; + if (metadata != nullptr && metadata->additive_fields != nullptr) { + additiveFields.assign(metadata->additive_fields, + metadata->additive_fields + metadata->additive_fields_size); + } + + std::lock_guard lg(pullAtomMutex); + const android::sp statsService = getStatsServiceLocked(); + if (statsService == nullptr) { + // Error - statsd not available + return; + } + + android::sp callbackBinder = new StatsPullAtomCallbackInternal( + callback, cookie, coolDownNs, timeoutNs, additiveFields); + mPullers[atom_tag] = callbackBinder; + statsService->registerNativePullAtomCallback(atom_tag, coolDownNs, timeoutNs, additiveFields, + callbackBinder); +}