Changes pulled data to use Parcel objects.
Previously, pulled data was returned as a string. We instead return the data as an array of StatsLogEventWrapper, which encodes using the binary-encoded format liblog uses. StatsD uses the same parsing as for pushed events to convert these. This CL also fixes the parsing of log_msg since the strings were previously emptied before we had a chance to read the values. Note that the cpp-aidl can't support List of Parcelable, so we have to return the results as an array. Test: Manual using the new command in StatsService to print results. Also created a new unit-test by creating a dummy pull code of -1, but this test is deleted since it required creating a fake output in StatsCompanionService. Change-Id: I1cfb9ea081a59292a60e934e8527adc40982ed80
This commit is contained in:
@@ -579,6 +579,8 @@ LOCAL_SRC_FILES += \
|
||||
|
||||
LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
|
||||
|
||||
LOCAL_AIDL_INCLUDES += core/java/android/os/StatsLogEventWrapper.aidl
|
||||
|
||||
LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java
|
||||
LOCAL_SRC_FILES += \
|
||||
lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl \
|
||||
|
||||
@@ -49,7 +49,8 @@ statsd_common_src := \
|
||||
src/stats_util.cpp
|
||||
|
||||
statsd_common_c_includes := \
|
||||
$(LOCAL_PATH)/src
|
||||
$(LOCAL_PATH)/src \
|
||||
$(LOCAL_PATH)/../../libs/services/include
|
||||
|
||||
statsd_common_aidl_includes := \
|
||||
$(LOCAL_PATH)/../../core/java
|
||||
@@ -95,7 +96,7 @@ else
|
||||
endif
|
||||
LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
|
||||
|
||||
LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
|
||||
LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
|
||||
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
|
||||
@@ -117,7 +118,7 @@ LOCAL_MODULE := statsd_test
|
||||
LOCAL_COMPATIBILITY_SUITE := device-tests
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
|
||||
LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
|
||||
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
|
||||
|
||||
LOCAL_CFLAGS += \
|
||||
|
||||
@@ -63,9 +63,9 @@ void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
|
||||
|
||||
// ======================================================================
|
||||
StatsService::StatsService(const sp<Looper>& handlerLooper)
|
||||
: mStatsPullerManager(),
|
||||
mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better
|
||||
: mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Put this comment somewhere better
|
||||
{
|
||||
mStatsPullerManager = new StatsPullerManager();
|
||||
mUidMap = new UidMap();
|
||||
mConfigManager = new ConfigManager();
|
||||
mProcessor = new StatsLogProcessor(mUidMap);
|
||||
@@ -193,6 +193,10 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>&
|
||||
if (!args[0].compare(String8("dump-report"))) {
|
||||
return cmd_dump_report(out, err, args);
|
||||
}
|
||||
|
||||
if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
|
||||
return cmd_print_pulled_metrics(out, args);
|
||||
}
|
||||
}
|
||||
|
||||
print_cmd_help(out);
|
||||
@@ -210,6 +214,11 @@ void StatsService::print_cmd_help(FILE* out) {
|
||||
fprintf(out, " Prints the UID, app name, version mapping.\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "usage: adb shell cmds stats pull-source [int] \n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "usage: adb shell cmd stats config remove [UID] NAME\n");
|
||||
fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
|
||||
fprintf(out, "\n");
|
||||
@@ -353,6 +362,16 @@ status_t StatsService::cmd_print_uid_map(FILE* out) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
|
||||
int s = atoi(args[1].c_str());
|
||||
auto stats = mStatsPullerManager->Pull(s);
|
||||
for (const auto& it : stats) {
|
||||
fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
|
||||
}
|
||||
fprintf(out, "Pull from %d: Received %zu elements\n", s, stats.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
|
||||
const vector<String16>& app) {
|
||||
if (DEBUG) ALOGD("StatsService::informAllUidData was called");
|
||||
@@ -414,10 +433,6 @@ Status StatsService::informPollAlarmFired() {
|
||||
|
||||
if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
|
||||
// TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
|
||||
String16 output = mStatsPullerManager.pull(StatsPullerManager::KERNEL_WAKELOCKS);
|
||||
// TODO: do something useful with the output instead of writing a string to screen.
|
||||
ALOGD("%s", String8(output).string());
|
||||
ALOGD("%d", int(output.size()));
|
||||
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
@@ -120,6 +120,11 @@ private:
|
||||
*/
|
||||
status_t cmd_print_uid_map(FILE* out);
|
||||
|
||||
/**
|
||||
* Print contents of a pulled metrics source.
|
||||
*/
|
||||
status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args);
|
||||
|
||||
/**
|
||||
* Update a configuration.
|
||||
*/
|
||||
@@ -132,9 +137,8 @@ private:
|
||||
|
||||
/**
|
||||
* Fetches external metrics.
|
||||
* TODO: This should be an sp<>
|
||||
*/
|
||||
StatsPullerManager mStatsPullerManager;
|
||||
sp<StatsPullerManager> mStatsPullerManager;
|
||||
|
||||
/**
|
||||
* Tracks the configurations that have been passed to statsd.
|
||||
|
||||
@@ -37,9 +37,9 @@ const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 20;
|
||||
|
||||
// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
|
||||
// let StatsCompanionService handle that and send the data back.
|
||||
String16 KernelWakelockPuller::pull() {
|
||||
vector<StatsLogEventWrapper> KernelWakelockPuller::pull() {
|
||||
sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
|
||||
String16 returned_value("");
|
||||
vector<StatsLogEventWrapper> returned_value;
|
||||
if (statsCompanion != NULL) {
|
||||
Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS,
|
||||
&returned_value);
|
||||
@@ -47,12 +47,10 @@ String16 KernelWakelockPuller::pull() {
|
||||
ALOGW("error pulling kernel wakelock");
|
||||
}
|
||||
ALOGD("KernelWakelockPuller::pull succeeded!");
|
||||
// TODO: remove this when we integrate into aggregation chain.
|
||||
ALOGD("%s", String8(returned_value).string());
|
||||
return returned_value;
|
||||
} else {
|
||||
ALOGW("statsCompanion not found!");
|
||||
return String16();
|
||||
return returned_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
// a number of stats need to be pulled from StatsCompanionService
|
||||
//
|
||||
const static int PULL_CODE_KERNEL_WAKELOCKS;
|
||||
String16 pull() override;
|
||||
vector<StatsLogEventWrapper> pull() override;
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
9
cmds/statsd/src/external/StatsPuller.h
vendored
9
cmds/statsd/src/external/StatsPuller.h
vendored
@@ -17,7 +17,12 @@
|
||||
#ifndef STATSD_STATSPULLER_H
|
||||
#define STATSD_STATSPULLER_H
|
||||
|
||||
#include <android/os/StatsLogEventWrapper.h>
|
||||
#include <utils/String16.h>
|
||||
#include <vector>
|
||||
|
||||
using android::os::StatsLogEventWrapper;
|
||||
using std::vector;
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
@@ -26,8 +31,8 @@ namespace statsd {
|
||||
class StatsPuller {
|
||||
public:
|
||||
virtual ~StatsPuller(){};
|
||||
// use string for now, until we figure out how to integrate into the aggregation path
|
||||
virtual String16 pull() = 0;
|
||||
|
||||
virtual vector<StatsLogEventWrapper> pull() = 0;
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
|
||||
25
cmds/statsd/src/external/StatsPullerManager.cpp
vendored
25
cmds/statsd/src/external/StatsPullerManager.cpp
vendored
@@ -21,6 +21,11 @@
|
||||
#include "KernelWakelockPuller.h"
|
||||
#include "StatsService.h"
|
||||
#include "external/StatsPullerManager.h"
|
||||
#include "logd/LogEvent.h"
|
||||
#include <cutils/log.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace android;
|
||||
|
||||
@@ -35,13 +40,27 @@ StatsPullerManager::StatsPullerManager() {
|
||||
{static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
|
||||
}
|
||||
|
||||
String16 StatsPullerManager::pull(int pullCode) {
|
||||
vector<std::shared_ptr<LogEvent>> StatsPullerManager::Pull(int pullCode) {
|
||||
if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
|
||||
|
||||
vector<std::shared_ptr<LogEvent>> ret;
|
||||
if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
|
||||
return (mStatsPullers.find(pullCode)->second)->pull();
|
||||
vector<StatsLogEventWrapper> outputs = (mStatsPullers.find(pullCode)->second)->pull();
|
||||
for (const StatsLogEventWrapper& it : outputs) {
|
||||
log_msg tmp;
|
||||
tmp.entry_v1.len = it.bytes.size();
|
||||
// Manually set the header size to 28 bytes to match the pushed log events.
|
||||
tmp.entry.hdr_size = 28;
|
||||
// And set the received bytes starting after the 28 bytes reserved for header.
|
||||
std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + 28);
|
||||
std::shared_ptr<LogEvent> evt = std::make_shared<LogEvent>(tmp);
|
||||
ret.push_back(evt);
|
||||
// ret.emplace_back(tmp);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
ALOGD("Unknown pull code %d", pullCode);
|
||||
return String16();
|
||||
return ret; // Return early since we don't know what to pull.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <utils/String16.h>
|
||||
#include <unordered_map>
|
||||
#include "external/StatsPuller.h"
|
||||
#include "logd/LogEvent.h"
|
||||
#include "matchers/matcher_util.h"
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
@@ -27,7 +29,7 @@ namespace statsd {
|
||||
|
||||
const static int KERNEL_WAKELOCKS = 1;
|
||||
|
||||
class StatsPullerManager {
|
||||
class StatsPullerManager : public virtual RefBase {
|
||||
public:
|
||||
// Enums of pulled data types (pullCodes)
|
||||
// These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
|
||||
@@ -35,7 +37,8 @@ public:
|
||||
const static int KERNEL_WAKELOCKS;
|
||||
StatsPullerManager();
|
||||
|
||||
String16 pull(const int pullCode);
|
||||
// We return a vector of shared_ptr since LogEvent's copy constructor is not available.
|
||||
vector<std::shared_ptr<LogEvent>> Pull(const int pullCode);
|
||||
|
||||
private:
|
||||
std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
|
||||
|
||||
@@ -23,29 +23,30 @@ namespace os {
|
||||
namespace statsd {
|
||||
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
|
||||
LogEvent::LogEvent(const log_msg& msg) {
|
||||
init(msg);
|
||||
// We need to keep a copy of the android_log_event_list owned by this instance so that the char*
|
||||
// for strings is not cleared before we can read them.
|
||||
LogEvent::LogEvent(log_msg msg) : mList(msg) {
|
||||
init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList);
|
||||
}
|
||||
|
||||
LogEvent::LogEvent(int64_t timestampNs, android_log_event_list* reader) {
|
||||
init(timestampNs, reader);
|
||||
LogEvent::LogEvent(int tag) : mList(tag) {
|
||||
}
|
||||
|
||||
LogEvent::~LogEvent() {
|
||||
}
|
||||
|
||||
void LogEvent::init() {
|
||||
mList.convert_to_reader();
|
||||
init(mTimestampNs, &mList);
|
||||
}
|
||||
|
||||
/**
|
||||
* The elements of each log event are stored as a vector of android_log_list_elements.
|
||||
* The goal is to do as little preprocessing as possible, because we read a tiny fraction
|
||||
* of the elements that are written to the log.
|
||||
*/
|
||||
void LogEvent::init(const log_msg& msg) {
|
||||
|
||||
android_log_event_list list(const_cast<log_msg&>(msg));
|
||||
init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &list);
|
||||
}
|
||||
|
||||
void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
|
||||
mTimestampNs = timestampNs;
|
||||
mTagId = reader->tag();
|
||||
@@ -79,6 +80,10 @@ void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) {
|
||||
} while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
|
||||
}
|
||||
|
||||
android_log_event_list* LogEvent::GetAndroidLogEventList() {
|
||||
return &mList;
|
||||
}
|
||||
|
||||
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
|
||||
if (key < 1 || (key - 1) >= mElements.size()) {
|
||||
*err = BAD_INDEX;
|
||||
@@ -109,7 +114,8 @@ const char* LogEvent::GetString(size_t key, status_t* err) const {
|
||||
*err = BAD_TYPE;
|
||||
return NULL;
|
||||
}
|
||||
return elem.data.string;
|
||||
// Need to add the '/0' at the end by specifying the length of the string.
|
||||
return string(elem.data.string, elem.len).c_str();
|
||||
}
|
||||
|
||||
bool LogEvent::GetBool(size_t key, status_t* err) const {
|
||||
@@ -189,7 +195,8 @@ string LogEvent::ToString() const {
|
||||
} else if (elem.type == EVENT_TYPE_FLOAT) {
|
||||
result << elem.data.float32;
|
||||
} else if (elem.type == EVENT_TYPE_STRING) {
|
||||
result << elem.data.string;
|
||||
// Need to add the '/0' at the end by specifying the length of the string.
|
||||
result << string(elem.data.string, elem.len).c_str();
|
||||
}
|
||||
}
|
||||
result << " }";
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <log/log_event_list.h>
|
||||
#include <log/log_read.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -40,12 +41,16 @@ public:
|
||||
/**
|
||||
* Read a LogEvent from a log_msg.
|
||||
*/
|
||||
explicit LogEvent(const log_msg& msg);
|
||||
explicit LogEvent(log_msg msg);
|
||||
|
||||
/**
|
||||
* Read a LogEvent from an android_log_context.
|
||||
* Constructs a LogEvent with the specified tag and creates an android_log_event_list in write
|
||||
* mode. Obtain this list with the getter. Make sure to call init() before attempting to read
|
||||
* any of the values. This constructor is useful for unit-testing since we can't pass in an
|
||||
* android_log_event_list since there is no copy constructor or assignment operator available.
|
||||
*/
|
||||
explicit LogEvent(int64_t timestampNs, android_log_event_list* reader);
|
||||
explicit LogEvent(int tag);
|
||||
|
||||
~LogEvent();
|
||||
|
||||
/**
|
||||
@@ -85,6 +90,19 @@ public:
|
||||
*/
|
||||
KeyValuePair GetKeyValueProto(size_t key) const;
|
||||
|
||||
/**
|
||||
* A pointer to the contained log_event_list.
|
||||
*
|
||||
* @return The android_log_event_list contained within.
|
||||
*/
|
||||
android_log_event_list* GetAndroidLogEventList();
|
||||
|
||||
/**
|
||||
* Used with the constructor where tag is passed in. Converts the log_event_list to read mode
|
||||
* and prepares the list for reading.
|
||||
*/
|
||||
void init();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Don't copy, it's slower. If we really need this we can add it but let's try to
|
||||
@@ -103,6 +121,8 @@ private:
|
||||
void init(int64_t timestampNs, android_log_event_list* reader);
|
||||
|
||||
vector<android_log_list_element> mElements;
|
||||
// Need a copy of the android_log_event_list so the strings are not cleared.
|
||||
android_log_event_list mList;
|
||||
long mTimestampNs;
|
||||
int mTagId;
|
||||
};
|
||||
|
||||
@@ -42,12 +42,10 @@ TEST(LogEntryMatcherTest, TestSimpleMatcher) {
|
||||
auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
|
||||
simpleMatcher->set_tag(TAG_ID);
|
||||
|
||||
// Set up the event
|
||||
android_log_event_list list(TAG_ID);
|
||||
LogEvent event(TAG_ID);
|
||||
|
||||
// Convert to a LogEvent
|
||||
list.convert_to_reader();
|
||||
LogEvent event(999, &list);
|
||||
event.init();
|
||||
|
||||
// Test
|
||||
EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
|
||||
@@ -64,13 +62,13 @@ TEST(LogEntryMatcherTest, TestBoolMatcher) {
|
||||
keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
|
||||
|
||||
// Set up the event
|
||||
android_log_event_list list(TAG_ID);
|
||||
list << true;
|
||||
list << false;
|
||||
LogEvent event(TAG_ID);
|
||||
auto list = event.GetAndroidLogEventList();
|
||||
*list << true;
|
||||
*list << false;
|
||||
|
||||
// Convert to a LogEvent
|
||||
list.convert_to_reader();
|
||||
LogEvent event(999, &list);
|
||||
event.init();
|
||||
|
||||
// Test
|
||||
keyValue1->set_eq_bool(true);
|
||||
@@ -100,12 +98,12 @@ TEST(LogEntryMatcherTest, TestStringMatcher) {
|
||||
keyValue->set_eq_string("some value");
|
||||
|
||||
// Set up the event
|
||||
android_log_event_list list(TAG_ID);
|
||||
list << "some value";
|
||||
LogEvent event(TAG_ID);
|
||||
auto list = event.GetAndroidLogEventList();
|
||||
*list << "some value";
|
||||
|
||||
// Convert to a LogEvent
|
||||
list.convert_to_reader();
|
||||
LogEvent event(999, &list);
|
||||
event.init();
|
||||
|
||||
// Test
|
||||
EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
|
||||
@@ -121,12 +119,11 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
|
||||
keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
|
||||
|
||||
// Set up the event
|
||||
android_log_event_list list(TAG_ID);
|
||||
list << 11;
|
||||
LogEvent event(TAG_ID);
|
||||
auto list = event.GetAndroidLogEventList();
|
||||
*list << 11;
|
||||
|
||||
// Convert to a LogEvent
|
||||
list.convert_to_reader();
|
||||
LogEvent event(999, &list);
|
||||
event.init();
|
||||
|
||||
// Test
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.os.StatsLogEventWrapper;
|
||||
|
||||
/**
|
||||
* Binder interface to communicate with the Java-based statistics service helper.
|
||||
* {@hide}
|
||||
@@ -50,5 +52,5 @@ interface IStatsCompanionService {
|
||||
oneway void cancelPollingAlarms();
|
||||
|
||||
/** Pull the specified data. Results will be sent to statsd when complete. */
|
||||
String pullData(int pullCode);
|
||||
StatsLogEventWrapper[] pullData(int pullCode);
|
||||
}
|
||||
|
||||
20
core/java/android/os/StatsLogEventWrapper.aidl
Normal file
20
core/java/android/os/StatsLogEventWrapper.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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;
|
||||
|
||||
/** @hide */
|
||||
parcelable StatsLogEventWrapper cpp_header "android/os/StatsLogEventWrapper.h";
|
||||
141
core/java/android/os/StatsLogEventWrapper.java
Normal file
141
core/java/android/os/StatsLogEventWrapper.java
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Wrapper class for sending data from Android OS to StatsD.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class StatsLogEventWrapper implements Parcelable {
|
||||
private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
|
||||
|
||||
// Below are constants copied from log/log.h
|
||||
private static final int EVENT_TYPE_INT = 0; /* int32_t */
|
||||
private static final int EVENT_TYPE_LONG = 1; /* int64_t */
|
||||
private static final int EVENT_TYPE_STRING = 2;
|
||||
private static final int EVENT_TYPE_LIST = 3;
|
||||
private static final int EVENT_TYPE_FLOAT = 4;
|
||||
|
||||
/**
|
||||
* Creates a log_event that is binary-encoded as implemented in
|
||||
* system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
|
||||
* for pushed and pulled data. The write* methods must be called in the same order as their
|
||||
* field number. There is no checking that the correct number of write* methods is called.
|
||||
* We also write an END_LIST character before beginning to write to parcel, but this END_LIST
|
||||
* may be unnecessary.
|
||||
*
|
||||
* @param tag The integer representing the tag for this event.
|
||||
* @param fields The number of fields specified in this event.
|
||||
*/
|
||||
public StatsLogEventWrapper(int tag, int fields) {
|
||||
// Write four bytes from tag, starting with least-significant bit.
|
||||
write4Bytes(tag);
|
||||
mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
|
||||
mStorage.write(fields); // Indicate number of elements in this list.
|
||||
}
|
||||
|
||||
/**
|
||||
* Boilerplate for Parcel.
|
||||
*/
|
||||
public static final Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
|
||||
Parcelable.Creator<StatsLogEventWrapper>() {
|
||||
public StatsLogEventWrapper createFromParcel(Parcel in) {
|
||||
return new StatsLogEventWrapper(in);
|
||||
}
|
||||
|
||||
public StatsLogEventWrapper[] newArray(int size) {
|
||||
return new StatsLogEventWrapper[size];
|
||||
}
|
||||
};
|
||||
|
||||
private void write4Bytes(int val) {
|
||||
mStorage.write(val);
|
||||
mStorage.write(val >>> 8);
|
||||
mStorage.write(val >>> 16);
|
||||
mStorage.write(val >>> 24);
|
||||
}
|
||||
|
||||
private void write8Bytes(long val) {
|
||||
write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
|
||||
write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds 32-bit integer to output.
|
||||
*/
|
||||
public void writeInt(int val) {
|
||||
mStorage.write(EVENT_TYPE_INT);
|
||||
write4Bytes(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds 64-bit long to output.
|
||||
*/
|
||||
public void writeLong(long val) {
|
||||
mStorage.write(EVENT_TYPE_LONG);
|
||||
write8Bytes(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a 4-byte floating point value to output.
|
||||
*/
|
||||
public void writeFloat(float val) {
|
||||
int v = Float.floatToIntBits(val);
|
||||
mStorage.write(EVENT_TYPE_FLOAT);
|
||||
write4Bytes(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string to the output.
|
||||
*/
|
||||
public void writeString(String val) {
|
||||
mStorage.write(EVENT_TYPE_STRING);
|
||||
write4Bytes(val.length());
|
||||
byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
|
||||
mStorage.write(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
private StatsLogEventWrapper(Parcel in) {
|
||||
readFromParcel(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the stored fields to a byte array. Will first write a new-line character to denote
|
||||
* END_LIST before writing contents to byte array.
|
||||
*/
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
mStorage.write(10); // new-line character is same as END_LIST
|
||||
out.writeByteArray(mStorage.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Not implemented.
|
||||
*/
|
||||
public void readFromParcel(Parcel in) {
|
||||
// Not needed since this java class is for sending to statsd only.
|
||||
}
|
||||
|
||||
/**
|
||||
* Boilerplate for Parcel.
|
||||
*/
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,8 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libservices
|
||||
LOCAL_SRC_FILES := \
|
||||
../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
|
||||
src/os/DropBoxManager.cpp
|
||||
src/os/DropBoxManager.cpp \
|
||||
src/os/StatsLogEventWrapper.cpp
|
||||
|
||||
LOCAL_AIDL_INCLUDES := \
|
||||
$(LOCAL_PATH)/../../core/java
|
||||
|
||||
47
libs/services/include/android/os/StatsLogEventWrapper.h
Normal file
47
libs/services/include/android/os/StatsLogEventWrapper.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
#ifndef STATS_LOG_EVENT_WRAPPER_H
|
||||
#define STATS_LOG_EVENT_WRAPPER_H
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <binder/Parcelable.h>
|
||||
#include <binder/Status.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
|
||||
// Represents a parcelable object. Only used to send data from Android OS to statsd.
|
||||
class StatsLogEventWrapper : public android::Parcelable {
|
||||
public:
|
||||
StatsLogEventWrapper();
|
||||
|
||||
StatsLogEventWrapper(StatsLogEventWrapper&& in) = default;
|
||||
|
||||
android::status_t writeToParcel(android::Parcel* out) const;
|
||||
|
||||
android::status_t readFromParcel(const android::Parcel* in);
|
||||
|
||||
// These are public for ease of conversion.
|
||||
std::vector<uint8_t> bytes;
|
||||
};
|
||||
} // Namespace os
|
||||
} // Namespace android
|
||||
|
||||
|
||||
#endif // STATS_LOG_EVENT_WRAPPER_H
|
||||
|
||||
45
libs/services/src/os/StatsLogEventWrapper.cpp
Normal file
45
libs/services/src/os/StatsLogEventWrapper.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 <android/os/StatsLogEventWrapper.h>
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <binder/Parcelable.h>
|
||||
#include <binder/Status.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <vector>
|
||||
|
||||
using android::Parcel;
|
||||
using android::Parcelable;
|
||||
using android::status_t;
|
||||
using std::vector;
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
|
||||
StatsLogEventWrapper::StatsLogEventWrapper(){};
|
||||
|
||||
status_t StatsLogEventWrapper::writeToParcel(Parcel* out) const {
|
||||
out->writeByteVector(bytes);
|
||||
return ::android::NO_ERROR;
|
||||
};
|
||||
|
||||
status_t StatsLogEventWrapper::readFromParcel(const Parcel* in) {
|
||||
in->readByteVector(&bytes);
|
||||
return ::android::NO_ERROR;
|
||||
};
|
||||
|
||||
} // Namespace os
|
||||
} // Namespace android
|
||||
@@ -33,12 +33,14 @@ import android.os.IStatsManager;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.StatsLogEventWrapper;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.os.KernelWakelockReader;
|
||||
import com.android.internal.os.KernelWakelockStats;
|
||||
@@ -49,6 +51,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Helper service for statsd (the native stats management service in cmds/statsd/).
|
||||
* Used for registering and receiving alarms on behalf of statsd.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
@@ -90,7 +93,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
// Needed since the new user basically has a version of every app.
|
||||
informAllUidsLocked(context);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
|
||||
Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
|
||||
forgetEverything();
|
||||
}
|
||||
}
|
||||
@@ -99,9 +102,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
|
||||
}
|
||||
|
||||
private final static int[] toIntArray(List<Integer> list){
|
||||
private final static int[] toIntArray(List<Integer> list) {
|
||||
int[] ret = new int[list.size()];
|
||||
for(int i = 0;i < ret.length;i++) {
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = list.get(i);
|
||||
}
|
||||
return ret;
|
||||
@@ -113,7 +116,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
final List<UserInfo> users = um.getUsers(true);
|
||||
if (DEBUG) {
|
||||
Slog.w(TAG, "Iterating over "+users.size() + " profiles.");
|
||||
Slog.w(TAG, "Iterating over " + users.size() + " profiles.");
|
||||
}
|
||||
|
||||
List<Integer> uids = new ArrayList();
|
||||
@@ -122,23 +125,23 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
|
||||
// Add in all the apps for every user/profile.
|
||||
for (UserInfo profile : users) {
|
||||
List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
|
||||
for (int j = 0; j < pi.size(); j++) {
|
||||
if (pi.get(j).applicationInfo != null) {
|
||||
uids.add(pi.get(j).applicationInfo.uid);
|
||||
versions.add(pi.get(j).versionCode);
|
||||
apps.add(pi.get(j).packageName);
|
||||
}
|
||||
}
|
||||
List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
|
||||
for (int j = 0; j < pi.size(); j++) {
|
||||
if (pi.get(j).applicationInfo != null) {
|
||||
uids.add(pi.get(j).applicationInfo.uid);
|
||||
versions.add(pi.get(j).versionCode);
|
||||
apps.add(pi.get(j).packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
|
||||
String[apps.size()]));
|
||||
String[apps.size()]));
|
||||
if (DEBUG) {
|
||||
Slog.w(TAG, "Sent data for "+uids.size() +" apps");
|
||||
Slog.w(TAG, "Sent data for " + uids.size() + " apps");
|
||||
}
|
||||
}
|
||||
|
||||
public final static class AppUpdateReceiver extends BroadcastReceiver {
|
||||
public final static class AppUpdateReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
|
||||
@@ -147,7 +150,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
* waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
|
||||
*/
|
||||
if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
|
||||
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
||||
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
||||
return; // Keep only replacing or normal add and remove.
|
||||
}
|
||||
synchronized (sStatsdLock) {
|
||||
@@ -180,9 +183,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
|
||||
public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
|
||||
@@ -200,7 +203,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
}
|
||||
// AlarmManager releases its own wakelock here.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final static class PollingAlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
@@ -220,7 +223,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
}
|
||||
// AlarmManager releases its own wakelock here.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
public void setAnomalyAlarm(long timestampMs) {
|
||||
@@ -286,33 +289,34 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
|
||||
|
||||
@Override // Binder call
|
||||
public String pullData(int pullCode) {
|
||||
public StatsLogEventWrapper[] pullData(int pullCode) {
|
||||
enforceCallingPermission();
|
||||
if (DEBUG) Slog.d(TAG, "Fetching " + pullCode);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Pulling " + pullCode);
|
||||
}
|
||||
|
||||
StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string
|
||||
List<StatsLogEventWrapper> ret = new ArrayList<>();
|
||||
switch (pullCode) {
|
||||
case PULL_CODE_KERNEL_WAKELOCKS:
|
||||
case PULL_CODE_KERNEL_WAKELOCKS: {
|
||||
final KernelWakelockStats wakelockStats =
|
||||
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
|
||||
|
||||
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
|
||||
String name = ent.getKey();
|
||||
KernelWakelockStats.Entry kws = ent.getValue();
|
||||
s.append("Wakelock ")
|
||||
.append(name)
|
||||
.append(", time=")
|
||||
.append(kws.mTotalTime)
|
||||
.append(", count=")
|
||||
.append(kws.mCount)
|
||||
.append('\n');
|
||||
StatsLogEventWrapper e = new StatsLogEventWrapper(101, 4);
|
||||
e.writeInt(kws.mCount);
|
||||
e.writeInt(kws.mVersion);
|
||||
e.writeLong(kws.mTotalTime);
|
||||
e.writeString(name);
|
||||
ret.add(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Slog.w(TAG, "No such pollable data as " + pullCode);
|
||||
return null;
|
||||
}
|
||||
return s.toString();
|
||||
return ret.toArray(new StatsLogEventWrapper[ret.size()]);
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@@ -331,7 +335,9 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
|
||||
// Lifecycle and related code
|
||||
|
||||
/** Fetches the statsd IBinder service */
|
||||
/**
|
||||
* Fetches the statsd IBinder service
|
||||
*/
|
||||
private static IStatsManager fetchStatsdService() {
|
||||
return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
|
||||
}
|
||||
@@ -363,13 +369,17 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/** Now that the android system is ready, StatsCompanion is ready too, so inform statsd. */
|
||||
/**
|
||||
* Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
|
||||
*/
|
||||
private void systemReady() {
|
||||
if (DEBUG) Slog.d(TAG, "Learned that systemReady");
|
||||
sayHiToStatsd();
|
||||
}
|
||||
|
||||
/** Tells statsd that statscompanion is ready. If the binder call returns, link to statsd. */
|
||||
/**
|
||||
* Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
|
||||
*/
|
||||
private void sayHiToStatsd() {
|
||||
synchronized (sStatsdLock) {
|
||||
if (sStatsd != null) {
|
||||
@@ -398,14 +408,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
|
||||
null);
|
||||
null);
|
||||
|
||||
// Setup receiver for user initialize (which happens once for a new user) and
|
||||
// if a user is removed.
|
||||
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
|
||||
filter.addAction(Intent.ACTION_USER_REMOVED);
|
||||
mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
|
||||
filter, null, null);
|
||||
filter, null, null);
|
||||
|
||||
// Pull the latest state of UID->app name, version mapping when statsd starts.
|
||||
informAllUidsLocked(mContext);
|
||||
|
||||
Reference in New Issue
Block a user