From 31eb67b3498d326659b2b164ff367a01a793d641 Mon Sep 17 00:00:00 2001 From: yro Date: Tue, 24 Oct 2017 13:33:21 -0700 Subject: [PATCH] Adds aidl definitions and their implementations for binder transfer of statsd entries to clients. This change only includes changes on statds side and does not include java library for clients to import. Java library will be a separate change as it requires system api review. Test: statsd, statsd_test Change-Id: I306c6e9687801668cc0145b12d38406bfe634775 --- Android.mk | 1 + cmds/statsd/Android.mk | 1 + cmds/statsd/src/StatsLogProcessor.cpp | 40 +++++++++++++++++++++++++++++-- cmds/statsd/src/StatsLogProcessor.h | 33 ++++++++++++++++++++++++- cmds/statsd/src/StatsService.cpp | 38 ++++++++++++++++++++++++++++- cmds/statsd/src/StatsService.h | 32 ++++++++++++++++++++++++- core/java/android/os/IStatsCallbacks.aidl | 25 +++++++++++++++++++ core/java/android/os/IStatsManager.aidl | 13 ++++++++++ 8 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 core/java/android/os/IStatsCallbacks.aidl diff --git a/Android.mk b/Android.mk index 648544a956a7..9890bb401404 100644 --- a/Android.mk +++ b/Android.mk @@ -270,6 +270,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IRecoverySystemProgressListener.aidl \ core/java/android/os/IRemoteCallback.aidl \ core/java/android/os/ISchedulingPolicyService.aidl \ + core/java/android/os/IStatsCallbacks.aidl \ core/java/android/os/IStatsCompanionService.aidl \ core/java/android/os/IStatsManager.aidl \ core/java/android/os/IThermalEventListener.aidl \ diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 8946aed58052..50f631184155 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -15,6 +15,7 @@ LOCAL_PATH:= $(call my-dir) statsd_common_src := \ + ../../core/java/android/os/IStatsCallbacks.aidl \ ../../core/java/android/os/IStatsCompanionService.aidl \ ../../core/java/android/os/IStatsManager.aidl \ src/stats_log.proto \ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index e7825cf75159..68f48a4f4c37 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -33,8 +33,9 @@ namespace android { namespace os { namespace statsd { -StatsLogProcessor::StatsLogProcessor(const sp& uidMap) - : m_dropbox_writer("all-logs"), mUidMap(uidMap) { +StatsLogProcessor::StatsLogProcessor(const sp& uidMap, + const std::function&)>& pushLog) + : m_dropbox_writer("all-logs"), mUidMap(uidMap), mPushLog(pushLog) { } StatsLogProcessor::~StatsLogProcessor() { @@ -91,6 +92,41 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { } } +void StatsLogProcessor::addEventMetricData(const EventMetricData& eventMetricData) { + // TODO: Replace this code when MetricsManager.onDumpReport() is ready to + // get a list of byte arrays. + flushIfNecessary(eventMetricData); + const int numBytes = eventMetricData.ByteSize(); + char buffer[numBytes]; + eventMetricData.SerializeToArray(&buffer[0], numBytes); + string bufferString(buffer, numBytes); + mEvents.push_back(bufferString); + mBufferSize += eventMetricData.ByteSize(); +} + +void StatsLogProcessor::flushIfNecessary(const EventMetricData& eventMetricData) { + if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) { + flush(); + } +} + +void StatsLogProcessor::flush() { + StatsLogReport logReport; + for (string eventBuffer : mEvents) { + EventMetricData eventFromBuffer; + eventFromBuffer.ParseFromString(eventBuffer); + EventMetricData* newEntry = logReport.mutable_event_metrics()->add_data(); + newEntry->CopyFrom(eventFromBuffer); + } + + const int numBytes = logReport.ByteSize(); + vector logReportBuffer(numBytes); + logReport.SerializeToArray(&logReportBuffer[0], numBytes); + mPushLog(logReportBuffer); + mEvents.clear(); + mBufferSize = 0; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 3cefd29fa026..08090c11f724 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -33,7 +33,8 @@ namespace statsd { class StatsLogProcessor : public ConfigListener { public: - StatsLogProcessor(const sp& uidMap); + StatsLogProcessor(const sp& uidMap, + const std::function&)>& pushLog); virtual ~StatsLogProcessor(); virtual void OnLogEvent(const LogEvent& event); @@ -44,6 +45,9 @@ public: // TODO: Once we have the ProtoOutputStream in c++, we can just return byte array. std::vector onDumpReport(const ConfigKey& key); + /* Request a flush through a binder call. */ + void flush(); + private: // TODO: use EventMetrics to log the events. DropboxWriter m_dropbox_writer; @@ -51,6 +55,33 @@ private: std::unordered_map> mMetricsManagers; sp mUidMap; // Reference to the UidMap to lookup app name and version for each uid. + + /* Max *serialized* size of the logs kept in memory before flushing through binder call. + Proto lite does not implement the SpaceUsed() function which gives the in memory byte size. + So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size + is higher than its serialized size. + */ + static const size_t kMaxSerializedBytes = 16 * 1024; + + /* List of data that was captured for a single metric over a given interval of time. */ + vector mEvents; + + /* Current *serialized* size of the logs kept in memory. + To save computation, we will not calculate the size of the StatsLogReport every time when a + new entry is added, which would recursively call ByteSize() on every log entry. Instead, we + keep the sum of all individual stats log entry sizes. The size of a proto is approximately + the sum of the size of all member protos. + */ + size_t mBufferSize = 0; + + /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush + the logs to dropbox if true. */ + void flushIfNecessary(const EventMetricData& eventMetricData); + + /* Append event metric data to StatsLogReport. */ + void addEventMetricData(const EventMetricData& eventMetricData); + + std::function&)> mPushLog; }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1faeee0a6554..604753ef54a0 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -68,7 +68,9 @@ StatsService::StatsService(const sp& handlerLooper) mStatsPullerManager = new StatsPullerManager(); mUidMap = new UidMap(); mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor(mUidMap); + mProcessor = new StatsLogProcessor(mUidMap, [this](const vector& log) { + pushLog(log); + }); mConfigManager->AddListener(mProcessor); @@ -507,6 +509,40 @@ void StatsService::OnLogEvent(const LogEvent& event) { mProcessor->OnLogEvent(event); } +Status StatsService::requestPush() { + mProcessor->flush(); + return Status::ok(); +} + +Status StatsService::pushLog(const vector& log) { + std::lock_guard lock(mLock); + for (size_t i = 0; i < mCallbacks.size(); i++) { + mCallbacks[i]->onReceiveLogs((vector*)&log); + } + return Status::ok(); +} + +Status StatsService::subscribeStatsLog(const sp& callback) { + std::lock_guard lock(mLock); + for (size_t i = 0; i < mCallbacks.size(); i++) { + if (mCallbacks[i] == callback) { + return Status::fromStatusT(-errno); + } + } + mCallbacks.add(callback); + IInterface::asBinder(callback)->linkToDeath(this); + return Status::ok(); +} + +void StatsService::binderDied(const wp& who) { + for (size_t i = 0; i < mCallbacks.size(); i++) { + if (IInterface::asBinder(mCallbacks[i]) == who) { + mCallbacks.removeAt(i); + break; + } + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 449a2b84bfd7..7f046584b2d0 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -24,6 +24,7 @@ #include "packages/UidMap.h" #include +#include #include #include #include @@ -42,7 +43,7 @@ namespace android { namespace os { namespace statsd { -class StatsService : public BnStatsManager, public LogListener { +class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient { public: StatsService(const sp& handlerLooper); virtual ~StatsService(); @@ -70,6 +71,22 @@ public: */ virtual void OnLogEvent(const LogEvent& event); + /** + * Binder call to force trigger pushLog. This would be called by callback + * clients. + */ + virtual Status requestPush() override; + + /** + * Pushes stats log entries from statsd to callback clients. + */ + Status pushLog(const vector& log); + + /** + * Binder call to listen to statsd to send stats log entries. + */ + virtual Status subscribeStatsLog(const sp& callbacks) override; + // TODO: public for testing since statsd doesn't run when system starts. Change to private // later. /** Inform statsCompanion that statsd is ready. */ @@ -78,6 +95,9 @@ public: /** Fetches and returns the StatsCompanionService. */ static sp getStatsCompanionService(); + /** IBinder::DeathRecipient */ + virtual void binderDied(const wp& who) override; + private: /** * Load system properties at init. @@ -159,6 +179,16 @@ private: * Whether this is an eng build. */ bool mEngBuild; + + /** + * Lock for callback handling. + */ + std::mutex mLock; + + /** + * Vector maintaining the list of callbacks for clients. + */ + Vector< sp > mCallbacks; }; } // namespace statsd diff --git a/core/java/android/os/IStatsCallbacks.aidl b/core/java/android/os/IStatsCallbacks.aidl new file mode 100644 index 000000000000..02e7cd3978c8 --- /dev/null +++ b/core/java/android/os/IStatsCallbacks.aidl @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2016, 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. + */ + +package android.os; + +/** + * Callback for Statsd to allow binder calls to clients. + * {@hide} + */ +interface IStatsCallbacks { + void onReceiveLogs(out byte[] log); +} diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index daacc4e832f9..480296c12e50 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -16,6 +16,8 @@ package android.os; +import android.os.IStatsCallbacks; + /** * Binder interface to communicate with the statistics management service. * {@hide} @@ -61,4 +63,15 @@ interface IStatsManager { * Inform stats that an app was removed. */ oneway void informOnePackageRemoved(in String app, in int uid); + + /** + * Trigger pushLog to force push stats log entries from statsd on client side. + */ + void requestPush(); + + /** + * Listen to statsd to send stats log entries. + * TODO: Limit callbacks with specific configurations. + */ + void subscribeStatsLog(IStatsCallbacks callbacks); } -- 2.11.0