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 \
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 \
namespace os {
namespace statsd {
-StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap)
- : m_dropbox_writer("all-logs"), mUidMap(uidMap) {
+StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
+ const std::function<void(const vector<uint8_t>&)>& pushLog)
+ : m_dropbox_writer("all-logs"), mUidMap(uidMap), mPushLog(pushLog) {
}
StatsLogProcessor::~StatsLogProcessor() {
}
}
+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<uint8_t> logReportBuffer(numBytes);
+ logReport.SerializeToArray(&logReportBuffer[0], numBytes);
+ mPushLog(logReportBuffer);
+ mEvents.clear();
+ mBufferSize = 0;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
class StatsLogProcessor : public ConfigListener {
public:
- StatsLogProcessor(const sp<UidMap>& uidMap);
+ StatsLogProcessor(const sp<UidMap>& uidMap,
+ const std::function<void(const vector<uint8_t>&)>& pushLog);
virtual ~StatsLogProcessor();
virtual void OnLogEvent(const LogEvent& event);
// TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
std::vector<StatsLogReport> 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;
std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;
sp<UidMap> 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<string> 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<void(const vector<uint8_t>&)> mPushLog;
};
} // namespace statsd
mStatsPullerManager = new StatsPullerManager();
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(mUidMap);
+ mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) {
+ pushLog(log);
+ });
mConfigManager->AddListener(mProcessor);
mProcessor->OnLogEvent(event);
}
+Status StatsService::requestPush() {
+ mProcessor->flush();
+ return Status::ok();
+}
+
+Status StatsService::pushLog(const vector<uint8_t>& log) {
+ std::lock_guard<std::mutex> lock(mLock);
+ for (size_t i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks[i]->onReceiveLogs((vector<uint8_t>*)&log);
+ }
+ return Status::ok();
+}
+
+Status StatsService::subscribeStatsLog(const sp<IStatsCallbacks>& callback) {
+ std::lock_guard<std::mutex> 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<IBinder>& 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
#include "packages/UidMap.h"
#include <android/os/BnStatsManager.h>
+#include <android/os/IStatsCallbacks.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
#include <binder/IShellCallback.h>
namespace os {
namespace statsd {
-class StatsService : public BnStatsManager, public LogListener {
+class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient {
public:
StatsService(const sp<Looper>& handlerLooper);
virtual ~StatsService();
*/
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<uint8_t>& log);
+
+ /**
+ * Binder call to listen to statsd to send stats log entries.
+ */
+ virtual Status subscribeStatsLog(const sp<IStatsCallbacks>& callbacks) override;
+
// TODO: public for testing since statsd doesn't run when system starts. Change to private
// later.
/** Inform statsCompanion that statsd is ready. */
/** Fetches and returns the StatsCompanionService. */
static sp<IStatsCompanionService> getStatsCompanionService();
+ /** IBinder::DeathRecipient */
+ virtual void binderDied(const wp<IBinder>& who) override;
+
private:
/**
* Load system properties at init.
* 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<IStatsCallbacks> > mCallbacks;
};
} // namespace statsd
--- /dev/null
+/**
+ * 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);
+}
package android.os;
+import android.os.IStatsCallbacks;
+
/**
* Binder interface to communicate with the statistics management service.
* {@hide}
* 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);
}