Statsd broadcast subscriber
Allows a uid that uploads a statsd config to additionally register a BroadcastSubscriber with statsd. If statsd detects an anomaly (according to the config's Alert), statsd can inform a BroadcastSubscriber provided in the config. The config uses a subscriberId (just an int) to identify the BroadcastSubscriber. It then uses StatsManager.setBroadcastSubscriber to associate that subscriberId with a given PendingIntent. Then, when the anomaly is detected, statsd sends a broadcast using that PendingIntent, alerting whoever was specified by the config/setBroadcastSubscriber. Bug: 70356901 Test: cts-tradefed run cts-dev -m CtsStatsdHostTestCases -t android.cts.statsd.alert.BroadcastSubscriberTests Change-Id: I4d9ea9a6c8a85e61fadfd99c1513c55abbadd5e9
This commit is contained in:
@@ -354,6 +354,19 @@ package android.app {
|
||||
method public org.json.JSONObject toJson() throws org.json.JSONException;
|
||||
}
|
||||
|
||||
public final class StatsManager {
|
||||
method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
|
||||
method public byte[] getData(long);
|
||||
method public byte[] getMetadata();
|
||||
method public boolean removeConfiguration(long);
|
||||
method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
|
||||
field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
|
||||
field public static final java.lang.String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
|
||||
field public static final java.lang.String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE";
|
||||
field public static final java.lang.String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID";
|
||||
field public static final java.lang.String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
|
||||
}
|
||||
|
||||
public class VrManager {
|
||||
method public void setAndBindVrCompositor(android.content.ComponentName);
|
||||
method public void setPersistentVrModeEnabled(boolean);
|
||||
@@ -3538,6 +3551,27 @@ package android.os {
|
||||
method public abstract void onResult(android.os.Bundle);
|
||||
}
|
||||
|
||||
public final class StatsDimensionsValue implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public boolean getBooleanValue();
|
||||
method public int getField();
|
||||
method public float getFloatValue();
|
||||
method public int getIntValue();
|
||||
method public long getLongValue();
|
||||
method public java.lang.String getStringValue();
|
||||
method public java.util.List<android.os.StatsDimensionsValue> getTupleValueList();
|
||||
method public int getValueType();
|
||||
method public boolean isValueType(int);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final int BOOLEAN_VALUE_TYPE = 5; // 0x5
|
||||
field public static final android.os.Parcelable.Creator<android.os.StatsDimensionsValue> CREATOR;
|
||||
field public static final int FLOAT_VALUE_TYPE = 6; // 0x6
|
||||
field public static final int INT_VALUE_TYPE = 3; // 0x3
|
||||
field public static final int LONG_VALUE_TYPE = 4; // 0x4
|
||||
field public static final int STRING_VALUE_TYPE = 2; // 0x2
|
||||
field public static final int TUPLE_VALUE_TYPE = 7; // 0x7
|
||||
}
|
||||
|
||||
public class SystemProperties {
|
||||
method public static java.lang.String get(java.lang.String);
|
||||
method public static java.lang.String get(java.lang.String, java.lang.String);
|
||||
@@ -4900,16 +4934,6 @@ package android.util {
|
||||
method public int getUid();
|
||||
}
|
||||
|
||||
public final class StatsManager {
|
||||
method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
|
||||
method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
|
||||
method public byte[] getData(java.lang.String);
|
||||
method public byte[] getData(long);
|
||||
method public byte[] getMetadata();
|
||||
method public boolean removeConfiguration(java.lang.String);
|
||||
method public boolean removeConfiguration(long);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.view {
|
||||
|
||||
@@ -62,6 +62,7 @@ statsd_common_src := \
|
||||
src/storage/StorageManager.cpp \
|
||||
src/StatsLogProcessor.cpp \
|
||||
src/StatsService.cpp \
|
||||
src/subscriber/SubscriberReporter.cpp \
|
||||
src/HashableDimensionKey.cpp \
|
||||
src/guardrail/MemoryLeakTrackUtil.cpp \
|
||||
src/guardrail/StatsdStats.cpp
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "guardrail/MemoryLeakTrackUtil.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "storage/StorageManager.h"
|
||||
#include "subscriber/SubscriberReporter.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
@@ -67,6 +68,7 @@ CompanionDeathRecipient::CompanionDeathRecipient(const sp<AnomalyMonitor>& anoma
|
||||
void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
|
||||
ALOGW("statscompanion service died");
|
||||
mAnomalyMonitor->setStatsCompanionService(nullptr);
|
||||
SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
@@ -681,6 +683,7 @@ Status StatsService::statsCompanionReady() {
|
||||
VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
|
||||
IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor));
|
||||
mAnomalyMonitor->setStatsCompanionService(statsCompanion);
|
||||
SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
|
||||
|
||||
return Status::ok();
|
||||
}
|
||||
@@ -743,7 +746,9 @@ Status StatsService::addConfiguration(int64_t key,
|
||||
Status StatsService::removeConfiguration(int64_t key, bool* success) {
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
if (checkCallingPermission(String16(kPermissionDump))) {
|
||||
mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), key));
|
||||
ConfigKey configKey(ipc->getCallingUid(), key);
|
||||
mConfigManager->RemoveConfig(configKey);
|
||||
SubscriberReporter::getInstance().removeConfig(configKey);
|
||||
*success = true;
|
||||
return Status::ok();
|
||||
} else {
|
||||
@@ -752,6 +757,42 @@ Status StatsService::removeConfiguration(int64_t key, bool* success) {
|
||||
}
|
||||
}
|
||||
|
||||
Status StatsService::setBroadcastSubscriber(int64_t configId,
|
||||
int64_t subscriberId,
|
||||
const sp<android::IBinder>& intentSender,
|
||||
bool* success) {
|
||||
VLOG("StatsService::setBroadcastSubscriber called.");
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
if (checkCallingPermission(String16(kPermissionDump))) {
|
||||
ConfigKey configKey(ipc->getCallingUid(), configId);
|
||||
SubscriberReporter::getInstance()
|
||||
.setBroadcastSubscriber(configKey, subscriberId, intentSender);
|
||||
*success = true;
|
||||
return Status::ok();
|
||||
} else {
|
||||
*success = false;
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
}
|
||||
|
||||
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
|
||||
int64_t subscriberId,
|
||||
bool* success) {
|
||||
VLOG("StatsService::unsetBroadcastSubscriber called.");
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
if (checkCallingPermission(String16(kPermissionDump))) {
|
||||
ConfigKey configKey(ipc->getCallingUid(), configId);
|
||||
SubscriberReporter::getInstance()
|
||||
.unsetBroadcastSubscriber(configKey, subscriberId);
|
||||
*success = true;
|
||||
return Status::ok();
|
||||
} else {
|
||||
*success = false;
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StatsService::binderDied(const wp <IBinder>& who) {
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,21 @@ public:
|
||||
*/
|
||||
virtual Status removeConfiguration(int64_t key, bool* success) override;
|
||||
|
||||
/**
|
||||
* Binder call to associate the given config's subscriberId with the given intentSender.
|
||||
* intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
|
||||
*/
|
||||
virtual Status setBroadcastSubscriber(int64_t configId,
|
||||
int64_t subscriberId,
|
||||
const sp<android::IBinder>& intentSender,
|
||||
bool* success) override;
|
||||
|
||||
/**
|
||||
* Binder call to unassociate the given config's subscriberId with any intentSender.
|
||||
*/
|
||||
virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId,
|
||||
bool* success) override;
|
||||
|
||||
// TODO: public for testing since statsd doesn't run when system starts. Change to private
|
||||
// later.
|
||||
/** Inform statsCompanion that statsd is ready. */
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "external/Perfetto.h"
|
||||
#include "guardrail/StatsdStats.h"
|
||||
#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
|
||||
#include "subscriber/SubscriberReporter.h"
|
||||
|
||||
#include <android/os/IIncidentManager.h>
|
||||
#include <android/os/IncidentReportArgs.h>
|
||||
@@ -233,6 +234,7 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
|
||||
}
|
||||
|
||||
std::set<int> incidentdSections;
|
||||
|
||||
for (const Subscription& subscription : mSubscriptions) {
|
||||
switch (subscription.subscriber_information_case()) {
|
||||
case Subscription::SubscriberInformationCase::kIncidentdDetails:
|
||||
@@ -243,6 +245,10 @@ void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
|
||||
case Subscription::SubscriberInformationCase::kPerfettoDetails:
|
||||
CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
|
||||
break;
|
||||
case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
|
||||
SubscriberReporter::getInstance()
|
||||
.alertBroadcastSubscriber(mConfigKey, subscription, key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -51,10 +51,8 @@ public:
|
||||
|
||||
// Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
|
||||
// and removes it from firedAlarms.
|
||||
// TODO: This will actually be called from a different thread, so make it thread-safe!
|
||||
// This means that almost every function in DurationAnomalyTracker needs to be locked.
|
||||
// But this should be done at the level of StatsLogProcessor, which needs to lock
|
||||
// mMetricsMangers anyway.
|
||||
// Note that this will generally be called from a different thread from the other functions;
|
||||
// the caller is responsible for thread safety.
|
||||
void informAlarmsFired(const uint64_t& timestampNs,
|
||||
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) override;
|
||||
|
||||
|
||||
147
cmds/statsd/src/subscriber/SubscriberReporter.cpp
Normal file
147
cmds/statsd/src/subscriber/SubscriberReporter.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#define DEBUG false // STOPSHIP if true
|
||||
#include "Log.h"
|
||||
|
||||
#include "SubscriberReporter.h"
|
||||
|
||||
using android::IBinder;
|
||||
using std::lock_guard;
|
||||
using std::unordered_map;
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
|
||||
int64_t subscriberId,
|
||||
const sp<IBinder>& intentSender) {
|
||||
VLOG("SubscriberReporter::setBroadcastSubscriber called.");
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
mIntentMap[configKey][subscriberId] = intentSender;
|
||||
}
|
||||
|
||||
void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
|
||||
int64_t subscriberId) {
|
||||
VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
auto subscriberMapIt = mIntentMap.find(configKey);
|
||||
if (subscriberMapIt != mIntentMap.end()) {
|
||||
subscriberMapIt->second.erase(subscriberId);
|
||||
if (subscriberMapIt->second.empty()) {
|
||||
mIntentMap.erase(configKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SubscriberReporter::removeConfig(const ConfigKey& configKey) {
|
||||
VLOG("SubscriberReporter::removeConfig called.");
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
mIntentMap.erase(configKey);
|
||||
}
|
||||
|
||||
void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
|
||||
const Subscription& subscription,
|
||||
const HashableDimensionKey& dimKey) const {
|
||||
// Reminder about ids:
|
||||
// subscription id - name of the Subscription (that ties the Alert to the broadcast)
|
||||
// subscription rule_id - the name of the Alert (that triggers the broadcast)
|
||||
// subscriber_id - name of the PendingIntent to use to send the broadcast
|
||||
// config uid - the uid that uploaded the config (and therefore gave the PendingIntent,
|
||||
// although the intent may be to broadcast to a different uid)
|
||||
// config id - the name of this config (for this particular uid)
|
||||
|
||||
VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
|
||||
lock_guard<std::mutex> lock(mLock);
|
||||
|
||||
if (!subscription.has_broadcast_subscriber_details()
|
||||
|| !subscription.broadcast_subscriber_details().has_subscriber_id()) {
|
||||
ALOGE("Broadcast subscriber does not have an id.");
|
||||
return;
|
||||
}
|
||||
int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
|
||||
|
||||
auto it1 = mIntentMap.find(configKey);
|
||||
if (it1 == mIntentMap.end()) {
|
||||
ALOGW("Cannot inform subscriber for missing config key %s ", configKey.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
auto it2 = it1->second.find(subscriberId);
|
||||
if (it2 == it1->second.end()) {
|
||||
ALOGW("Cannot inform subscriber of config %s for missing subscriberId %lld ",
|
||||
configKey.ToString().c_str(), (long long)subscriberId);
|
||||
return;
|
||||
}
|
||||
sendBroadcastLocked(it2->second, configKey, subscription, dimKey);
|
||||
}
|
||||
|
||||
void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
|
||||
const ConfigKey& configKey,
|
||||
const Subscription& subscription,
|
||||
const HashableDimensionKey& dimKey) const {
|
||||
VLOG("SubscriberReporter::sendBroadcastLocked called.");
|
||||
if (mStatsCompanionService == nullptr) {
|
||||
ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
|
||||
return;
|
||||
}
|
||||
mStatsCompanionService->sendSubscriberBroadcast(intentSender,
|
||||
configKey.GetUid(),
|
||||
configKey.GetId(),
|
||||
subscription.id(),
|
||||
subscription.rule_id(),
|
||||
protoToStatsDimensionsValue(dimKey));
|
||||
}
|
||||
|
||||
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
|
||||
const HashableDimensionKey& dimKey) {
|
||||
return protoToStatsDimensionsValue(dimKey.getDimensionsValue());
|
||||
}
|
||||
|
||||
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
|
||||
const DimensionsValue& protoDimsVal) {
|
||||
int32_t field = protoDimsVal.field();
|
||||
|
||||
switch (protoDimsVal.value_case()) {
|
||||
case DimensionsValue::ValueCase::kValueStr:
|
||||
return StatsDimensionsValue(field, String16(protoDimsVal.value_str().c_str()));
|
||||
case DimensionsValue::ValueCase::kValueInt:
|
||||
return StatsDimensionsValue(field, static_cast<int32_t>(protoDimsVal.value_int()));
|
||||
case DimensionsValue::ValueCase::kValueLong:
|
||||
return StatsDimensionsValue(field, static_cast<int64_t>(protoDimsVal.value_long()));
|
||||
case DimensionsValue::ValueCase::kValueBool:
|
||||
return StatsDimensionsValue(field, static_cast<bool>(protoDimsVal.value_bool()));
|
||||
case DimensionsValue::ValueCase::kValueFloat:
|
||||
return StatsDimensionsValue(field, static_cast<float>(protoDimsVal.value_float()));
|
||||
case DimensionsValue::ValueCase::kValueTuple:
|
||||
{
|
||||
int sz = protoDimsVal.value_tuple().dimensions_value_size();
|
||||
std::vector<StatsDimensionsValue> sdvVec(sz);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
sdvVec[i] = protoToStatsDimensionsValue(
|
||||
protoDimsVal.value_tuple().dimensions_value(i));
|
||||
}
|
||||
return StatsDimensionsValue(field, sdvVec);
|
||||
}
|
||||
default:
|
||||
ALOGW("protoToStatsDimensionsValue failed: illegal type.");
|
||||
return StatsDimensionsValue();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
117
cmds/statsd/src/subscriber/SubscriberReporter.h
Normal file
117
cmds/statsd/src/subscriber/SubscriberReporter.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 <android/os/IStatsCompanionService.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#include "config/ConfigKey.h"
|
||||
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription
|
||||
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" // DimensionsValue
|
||||
#include "android/os/StatsDimensionsValue.h"
|
||||
#include "HashableDimensionKey.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
// Reports information to subscribers.
|
||||
// Single instance shared across the process. All methods are thread safe.
|
||||
class SubscriberReporter {
|
||||
public:
|
||||
/** Get (singleton) instance of SubscriberReporter. */
|
||||
static SubscriberReporter& getInstance() {
|
||||
static SubscriberReporter subscriberReporter;
|
||||
return subscriberReporter;
|
||||
}
|
||||
|
||||
~SubscriberReporter(){};
|
||||
SubscriberReporter(SubscriberReporter const&) = delete;
|
||||
void operator=(SubscriberReporter const&) = delete;
|
||||
|
||||
/**
|
||||
* Tells SubscriberReporter what IStatsCompanionService to use.
|
||||
* May be nullptr, but SubscriberReporter will not send broadcasts for any calls
|
||||
* to alertBroadcastSubscriber that occur while nullptr.
|
||||
*/
|
||||
void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
|
||||
mStatsCompanionService = statsCompanionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
|
||||
* intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
|
||||
*/
|
||||
void setBroadcastSubscriber(const ConfigKey& configKey,
|
||||
int64_t subscriberId,
|
||||
const sp<android::IBinder>& intentSender);
|
||||
|
||||
/**
|
||||
* Erases any intentSender information from the given (configKey, subscriberId) pair.
|
||||
*/
|
||||
void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
|
||||
|
||||
/** Remove all information stored by SubscriberReporter about the given config. */
|
||||
void removeConfig(const ConfigKey& configKey);
|
||||
|
||||
/**
|
||||
* Sends a broadcast via the intentSender previously stored for the
|
||||
* given (configKey, subscriberId) pair by setBroadcastSubscriber.
|
||||
* Information about the subscriber, as well as information extracted from the dimKey, is sent.
|
||||
*/
|
||||
void alertBroadcastSubscriber(const ConfigKey& configKey,
|
||||
const Subscription& subscription,
|
||||
const HashableDimensionKey& dimKey) const;
|
||||
|
||||
private:
|
||||
SubscriberReporter() {};
|
||||
|
||||
mutable std::mutex mLock;
|
||||
|
||||
/** Binder interface for communicating with StatsCompanionService. */
|
||||
sp<IStatsCompanionService> mStatsCompanionService = nullptr;
|
||||
|
||||
/** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */
|
||||
std::unordered_map<ConfigKey,
|
||||
std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap;
|
||||
|
||||
/**
|
||||
* Sends a broadcast via the given intentSender (using mStatsCompanionService), along
|
||||
* with the information in the other parameters.
|
||||
*/
|
||||
void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
|
||||
const ConfigKey& configKey,
|
||||
const Subscription& subscription,
|
||||
const HashableDimensionKey& dimKey) const;
|
||||
|
||||
/** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
|
||||
static StatsDimensionsValue protoToStatsDimensionsValue(
|
||||
const DimensionsValue& protoDimsVal);
|
||||
|
||||
/** Converts a HashableDimensionKey to a StatsDimensionsValue. */
|
||||
static StatsDimensionsValue protoToStatsDimensionsValue(
|
||||
const HashableDimensionKey& dimKey);
|
||||
};
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
@@ -16,12 +16,12 @@
|
||||
package com.android.statsd.dogfood;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.StatsManager;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.util.StatsLog;
|
||||
import android.util.StatsManager;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.StatsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -34,7 +35,6 @@ import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.util.StatsLog;
|
||||
import android.util.StatsManager;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
238
core/java/android/app/StatsManager.java
Normal file
238
core/java/android/app/StatsManager.java
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright 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.app;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.IBinder;
|
||||
import android.os.IStatsManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Slog;
|
||||
|
||||
/**
|
||||
* API for statsd clients to send configurations and retrieve data.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class StatsManager extends android.util.StatsManager { // TODO: Remove the extends.
|
||||
IStatsManager mService;
|
||||
private static final String TAG = "StatsManager";
|
||||
|
||||
/** Long extra of uid that added the relevant stats config. */
|
||||
public static final String EXTRA_STATS_CONFIG_UID =
|
||||
"android.app.extra.STATS_CONFIG_UID";
|
||||
/** Long extra of the relevant stats config's configKey. */
|
||||
public static final String EXTRA_STATS_CONFIG_KEY =
|
||||
"android.app.extra.STATS_CONFIG_KEY";
|
||||
/** Long extra of the relevant statsd_config.proto's Subscription.id. */
|
||||
public static final String EXTRA_STATS_SUBSCRIPTION_ID =
|
||||
"android.app.extra.STATS_SUBSCRIPTION_ID";
|
||||
/** Long extra of the relevant statsd_config.proto's Subscription.rule_id. */
|
||||
public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID =
|
||||
"android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
|
||||
/**
|
||||
* Extra of a {@link android.os.StatsDimensionsValue} representing sliced dimension value
|
||||
* information.
|
||||
*/
|
||||
public static final String EXTRA_STATS_DIMENSIONS_VALUE =
|
||||
"android.app.extra.STATS_DIMENSIONS_VALUE";
|
||||
|
||||
/**
|
||||
* Constructor for StatsManagerClient.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public StatsManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients can send a configuration and simultaneously registers the name of a broadcast
|
||||
* receiver that listens for when it should request data.
|
||||
*
|
||||
* @param configKey An arbitrary integer that allows clients to track the configuration.
|
||||
* @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
|
||||
* dependencies eg, conditions and matchers).
|
||||
* @param pkg The package name to receive the broadcast.
|
||||
* @param cls The name of the class that receives the broadcast.
|
||||
* @return true if successful
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
if (service == null) {
|
||||
Slog.d(TAG, "Failed to find statsd when adding configuration");
|
||||
return false;
|
||||
}
|
||||
return service.addConfiguration(configKey, config, pkg, cls);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Failed to connect to statsd when adding configuration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a configuration from logging.
|
||||
*
|
||||
* @param configKey Configuration key to remove.
|
||||
* @return true if successful
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
public boolean removeConfiguration(long configKey) {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
if (service == null) {
|
||||
Slog.d(TAG, "Failed to find statsd when removing configuration");
|
||||
return false;
|
||||
}
|
||||
return service.removeConfiguration(configKey);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Failed to connect to statsd when removing configuration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PendingIntent to be used when broadcasting subscriber information to the given
|
||||
* subscriberId within the given config.
|
||||
*
|
||||
* <p>
|
||||
* Suppose that the calling uid has added a config with key configKey, and that in this config
|
||||
* it is specified that when a particular anomaly is detected, a broadcast should be sent to
|
||||
* a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
|
||||
* that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
|
||||
* when the anomaly is detected.
|
||||
*
|
||||
* <p>
|
||||
* When statsd sends the broadcast, the PendingIntent will used to send an intent with
|
||||
* information of
|
||||
* {@link #EXTRA_STATS_CONFIG_UID},
|
||||
* {@link #EXTRA_STATS_CONFIG_KEY},
|
||||
* {@link #EXTRA_STATS_SUBSCRIPTION_ID},
|
||||
* {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
|
||||
* {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
|
||||
*
|
||||
* <p>
|
||||
* This function can only be called by the owner (uid) of the config. It must be called each
|
||||
* time statsd starts. The config must have been added first (via addConfiguration()).
|
||||
*
|
||||
* @param configKey The integer naming the config to which this subscriber is attached.
|
||||
* @param subscriberId ID of the subscriber, as used in the config.
|
||||
* @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
|
||||
* associated with the given subscriberId. May be null, in which case
|
||||
* it undoes any previous setting of this subscriberId.
|
||||
* @return true if successful
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
public boolean setBroadcastSubscriber(long configKey,
|
||||
long subscriberId,
|
||||
PendingIntent pendingIntent) {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
if (service == null) {
|
||||
Slog.w(TAG, "Failed to find statsd when adding broadcast subscriber");
|
||||
return false;
|
||||
}
|
||||
if (pendingIntent != null) {
|
||||
// Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
|
||||
IBinder intentSender = pendingIntent.getTarget().asBinder();
|
||||
return service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
|
||||
} else {
|
||||
return service.unsetBroadcastSubscriber(configKey, subscriberId);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients can request data with a binder call. This getter is destructive and also clears
|
||||
* the retrieved metrics from statsd memory.
|
||||
*
|
||||
* @param configKey Configuration key to retrieve data from.
|
||||
* @return Serialized ConfigMetricsReportList proto. Returns null on failure.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
public byte[] getData(long configKey) {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
if (service == null) {
|
||||
Slog.d(TAG, "Failed to find statsd when getting data");
|
||||
return null;
|
||||
}
|
||||
return service.getData(configKey);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Failed to connecto statsd when getting data");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients can request metadata for statsd. Will contain stats across all configurations but not
|
||||
* the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
|
||||
* This getter is not destructive and will not reset any metrics/counters.
|
||||
*
|
||||
* @return Serialized StatsdStatsReport proto. Returns null on failure.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
public byte[] getMetadata() {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
if (service == null) {
|
||||
Slog.d(TAG, "Failed to find statsd when getting metadata");
|
||||
return null;
|
||||
}
|
||||
return service.getMetadata();
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Failed to connecto statsd when getting metadata");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
|
||||
@Override
|
||||
public void binderDied() {
|
||||
synchronized (this) {
|
||||
mService = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IStatsManager getIStatsManagerLocked() throws RemoteException {
|
||||
if (mService != null) {
|
||||
return mService;
|
||||
}
|
||||
mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
|
||||
if (mService != null) {
|
||||
mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
|
||||
}
|
||||
return mService;
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,6 @@ import android.telephony.TelephonyManager;
|
||||
import android.telephony.euicc.EuiccCardManager;
|
||||
import android.telephony.euicc.EuiccManager;
|
||||
import android.util.Log;
|
||||
import android.util.StatsManager;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -4121,7 +4121,7 @@ public abstract class Context {
|
||||
public static final String STATS_COMPANION_SERVICE = "statscompanion";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to retrieve an {@link android.stats.StatsManager}.
|
||||
* Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.os.StatsDimensionsValue;
|
||||
import android.os.StatsLogEventWrapper;
|
||||
|
||||
/**
|
||||
@@ -55,8 +56,17 @@ interface IStatsCompanionService {
|
||||
StatsLogEventWrapper[] pullData(int pullCode);
|
||||
|
||||
/** Send a broadcast to the specified pkg and class that it should getData now. */
|
||||
// TODO: Rename this and use a pending intent instead.
|
||||
oneway void sendBroadcast(String pkg, String cls);
|
||||
|
||||
/**
|
||||
* Requests StatsCompanionService to send a broadcast using the given intentSender
|
||||
* (which should cast to an IIntentSender), along with the other information specified.
|
||||
*/
|
||||
oneway void sendSubscriberBroadcast(in IBinder intentSender, long configUid, long configId,
|
||||
long subscriptionId, long subscriptionRuleId,
|
||||
in StatsDimensionsValue dimensionsValue);
|
||||
|
||||
/** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
|
||||
oneway void triggerUidSnapshot();
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ interface IStatsManager {
|
||||
/**
|
||||
* Sets a configuration with the specified config key and subscribes to updates for this
|
||||
* configuration key. Broadcasts will be sent if this configuration needs to be collected.
|
||||
* The configuration must be a wire-encoded StatsDConfig. The caller specifies the name of the
|
||||
* The configuration must be a wire-encoded StatsdConfig. The caller specifies the name of the
|
||||
* package and class that should receive these broadcasts.
|
||||
*
|
||||
* Returns if this configuration was correctly registered.
|
||||
@@ -95,4 +95,33 @@ interface IStatsManager {
|
||||
* Returns if this configuration key was removed.
|
||||
*/
|
||||
boolean removeConfiguration(in long configKey);
|
||||
|
||||
/**
|
||||
* Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
|
||||
* information to the given subscriberId within the given config.
|
||||
*
|
||||
* Suppose that the calling uid has added a config with key configKey, and that in this config
|
||||
* it is specified that when a particular anomaly is detected, a broadcast should be sent to
|
||||
* a BroadcastSubscriber with id subscriberId. This function links the given intentSender with
|
||||
* that subscriberId (for that config), so that this intentSender is used to send the broadcast
|
||||
* when the anomaly is detected.
|
||||
*
|
||||
* This function can only be called by the owner (uid) of the config. It must be called each
|
||||
* time statsd starts. Later calls overwrite previous calls; only one intentSender is stored.
|
||||
*
|
||||
* intentSender must be convertible into an IntentSender using IntentSender(IBinder)
|
||||
* and cannot be null.
|
||||
*
|
||||
* Returns true if successful.
|
||||
*/
|
||||
boolean setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
|
||||
|
||||
/**
|
||||
* Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
|
||||
* Any broadcasts associated with subscriberId will henceforth not be sent.
|
||||
* No-op if this (configKey, subsriberId) pair was not associated with an IntentSender.
|
||||
*
|
||||
* Returns true if successful.
|
||||
*/
|
||||
boolean unsetBroadcastSubscriber(long configKey, long subscriberId);
|
||||
}
|
||||
|
||||
20
core/java/android/os/StatsDimensionsValue.aidl
Normal file
20
core/java/android/os/StatsDimensionsValue.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2018, 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 StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
|
||||
353
core/java/android/os/StatsDimensionsValue.java
Normal file
353
core/java/android/os/StatsDimensionsValue.java
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Copyright 2018 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 android.annotation.SystemApi;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Container for statsd dimension value information, corresponding to a
|
||||
* stats_log.proto's DimensionValue.
|
||||
*
|
||||
* This consists of a field (an int representing a statsd atom field)
|
||||
* and a value (which may be one of a number of types).
|
||||
*
|
||||
* <p>
|
||||
* Only a single value is held, and it is necessarily one of the following types:
|
||||
* {@link String}, int, long, boolean, float,
|
||||
* or tuple (i.e. {@link List} of {@code StatsDimensionsValue}).
|
||||
*
|
||||
* The type of value held can be retrieved using {@link #getValueType()}, which returns one of the
|
||||
* following ints, depending on the type of value:
|
||||
* <ul>
|
||||
* <li>{@link #STRING_VALUE_TYPE}</li>
|
||||
* <li>{@link #INT_VALUE_TYPE}</li>
|
||||
* <li>{@link #LONG_VALUE_TYPE}</li>
|
||||
* <li>{@link #BOOLEAN_VALUE_TYPE}</li>
|
||||
* <li>{@link #FLOAT_VALUE_TYPE}</li>
|
||||
* <li>{@link #TUPLE_VALUE_TYPE}</li>
|
||||
* </ul>
|
||||
* Alternatively, this can be determined using {@link #isValueType(int)} with one of these constants
|
||||
* as a parameter.
|
||||
* The value itself can be retrieved using the correct get...Value() function for its type.
|
||||
*
|
||||
* <p>
|
||||
* The field is always an int, and always exists; it can be obtained using {@link #getField()}.
|
||||
*
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class StatsDimensionsValue implements Parcelable {
|
||||
private static final String TAG = "StatsDimensionsValue";
|
||||
|
||||
// Values of the value type correspond to stats_log.proto's DimensionValue fields.
|
||||
// Keep constants in sync with services/include/android/os/StatsDimensionsValue.h.
|
||||
/** Indicates that this holds a String. */
|
||||
public static final int STRING_VALUE_TYPE = 2;
|
||||
/** Indicates that this holds an int. */
|
||||
public static final int INT_VALUE_TYPE = 3;
|
||||
/** Indicates that this holds a long. */
|
||||
public static final int LONG_VALUE_TYPE = 4;
|
||||
/** Indicates that this holds a boolean. */
|
||||
public static final int BOOLEAN_VALUE_TYPE = 5;
|
||||
/** Indicates that this holds a float. */
|
||||
public static final int FLOAT_VALUE_TYPE = 6;
|
||||
/** Indicates that this holds a List of StatsDimensionsValues. */
|
||||
public static final int TUPLE_VALUE_TYPE = 7;
|
||||
|
||||
/** Value of a stats_log.proto DimensionsValue.field. */
|
||||
private final int mField;
|
||||
|
||||
/** Type of stats_log.proto DimensionsValue.value, according to the VALUE_TYPEs above. */
|
||||
private final int mValueType;
|
||||
|
||||
/**
|
||||
* Value of a stats_log.proto DimensionsValue.value.
|
||||
* String, Integer, Long, Boolean, Float, or StatsDimensionsValue[].
|
||||
*/
|
||||
private final Object mValue; // immutable or array of immutables
|
||||
|
||||
/**
|
||||
* Creates a {@code StatsDimensionValue} from a parcel.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public StatsDimensionsValue(Parcel in) {
|
||||
mField = in.readInt();
|
||||
mValueType = in.readInt();
|
||||
mValue = readValueFromParcel(mValueType, in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the field, i.e. the tag of a statsd atom.
|
||||
*
|
||||
* @return the field
|
||||
*/
|
||||
public int getField() {
|
||||
return mField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the String held, if any.
|
||||
*
|
||||
* @return the {@link String} held if {@link #getValueType()} == {@link #STRING_VALUE_TYPE},
|
||||
* null otherwise
|
||||
*/
|
||||
public String getStringValue() {
|
||||
try {
|
||||
if (mValueType == STRING_VALUE_TYPE) return (String) mValue;
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "Failed to successfully get value", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the int held, if any.
|
||||
*
|
||||
* @return the int held if {@link #getValueType()} == {@link #INT_VALUE_TYPE}, 0 otherwise
|
||||
*/
|
||||
public int getIntValue() {
|
||||
try {
|
||||
if (mValueType == INT_VALUE_TYPE) return (Integer) mValue;
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "Failed to successfully get value", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the long held, if any.
|
||||
*
|
||||
* @return the long held if {@link #getValueType()} == {@link #LONG_VALUE_TYPE}, 0 otherwise
|
||||
*/
|
||||
public long getLongValue() {
|
||||
try {
|
||||
if (mValueType == LONG_VALUE_TYPE) return (Long) mValue;
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "Failed to successfully get value", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the boolean held, if any.
|
||||
*
|
||||
* @return the boolean held if {@link #getValueType()} == {@link #BOOLEAN_VALUE_TYPE},
|
||||
* false otherwise
|
||||
*/
|
||||
public boolean getBooleanValue() {
|
||||
try {
|
||||
if (mValueType == BOOLEAN_VALUE_TYPE) return (Boolean) mValue;
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "Failed to successfully get value", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the float held, if any.
|
||||
*
|
||||
* @return the float held if {@link #getValueType()} == {@link #FLOAT_VALUE_TYPE}, 0 otherwise
|
||||
*/
|
||||
public float getFloatValue() {
|
||||
try {
|
||||
if (mValueType == FLOAT_VALUE_TYPE) return (Float) mValue;
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "Failed to successfully get value", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the tuple, in the form of a {@link List} of {@link StatsDimensionsValue}, held,
|
||||
* if any.
|
||||
*
|
||||
* @return the {@link List} of {@link StatsDimensionsValue} held
|
||||
* if {@link #getValueType()} == {@link #TUPLE_VALUE_TYPE},
|
||||
* null otherwise
|
||||
*/
|
||||
public List<StatsDimensionsValue> getTupleValueList() {
|
||||
if (mValueType != TUPLE_VALUE_TYPE) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
StatsDimensionsValue[] orig = (StatsDimensionsValue[]) mValue;
|
||||
List<StatsDimensionsValue> copy = new ArrayList<>(orig.length);
|
||||
// Shallow copy since StatsDimensionsValue is immutable anyway
|
||||
for (int i = 0; i < orig.length; i++) {
|
||||
copy.add(orig[i]);
|
||||
}
|
||||
return copy;
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "Failed to successfully get value", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the constant representing the type of value stored, namely one of
|
||||
* <ul>
|
||||
* <li>{@link #STRING_VALUE_TYPE}</li>
|
||||
* <li>{@link #INT_VALUE_TYPE}</li>
|
||||
* <li>{@link #LONG_VALUE_TYPE}</li>
|
||||
* <li>{@link #BOOLEAN_VALUE_TYPE}</li>
|
||||
* <li>{@link #FLOAT_VALUE_TYPE}</li>
|
||||
* <li>{@link #TUPLE_VALUE_TYPE}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the constant representing the type of value stored
|
||||
*/
|
||||
public int getValueType() {
|
||||
return mValueType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the type of value stored is equal to the given type.
|
||||
*
|
||||
* @param valueType int representing the type of value stored, as used in {@link #getValueType}
|
||||
* @return true if {@link #getValueType()} is equal to {@code valueType}.
|
||||
*/
|
||||
public boolean isValueType(int valueType) {
|
||||
return mValueType == valueType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String representing the information in this StatsDimensionValue.
|
||||
* No guarantees are made about the format of this String.
|
||||
*
|
||||
* @return String representation
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
// Follows the format of statsd's dimension.h toString.
|
||||
public String toString() {
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(mField);
|
||||
sb.append(":");
|
||||
if (mValueType == TUPLE_VALUE_TYPE) {
|
||||
sb.append("{");
|
||||
StatsDimensionsValue[] sbvs = (StatsDimensionsValue[]) mValue;
|
||||
for (int i = 0; i < sbvs.length; i++) {
|
||||
sb.append(sbvs[i].toString());
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append("}");
|
||||
} else {
|
||||
sb.append(mValue.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "Failed to successfully get value", e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Parcelable Creator for StatsDimensionsValue.
|
||||
*/
|
||||
public static final Parcelable.Creator<StatsDimensionsValue> CREATOR = new
|
||||
Parcelable.Creator<StatsDimensionsValue>() {
|
||||
public StatsDimensionsValue createFromParcel(Parcel in) {
|
||||
return new StatsDimensionsValue(in);
|
||||
}
|
||||
|
||||
public StatsDimensionsValue[] newArray(int size) {
|
||||
return new StatsDimensionsValue[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(mField);
|
||||
out.writeInt(mValueType);
|
||||
writeValueToParcel(mValueType, mValue, out, flags);
|
||||
}
|
||||
|
||||
/** Writes mValue to a parcel. Returns true if succeeds. */
|
||||
private static boolean writeValueToParcel(int valueType, Object value, Parcel out, int flags) {
|
||||
try {
|
||||
switch (valueType) {
|
||||
case STRING_VALUE_TYPE:
|
||||
out.writeString((String) value);
|
||||
return true;
|
||||
case INT_VALUE_TYPE:
|
||||
out.writeInt((Integer) value);
|
||||
return true;
|
||||
case LONG_VALUE_TYPE:
|
||||
out.writeLong((Long) value);
|
||||
return true;
|
||||
case BOOLEAN_VALUE_TYPE:
|
||||
out.writeBoolean((Boolean) value);
|
||||
return true;
|
||||
case FLOAT_VALUE_TYPE:
|
||||
out.writeFloat((Float) value);
|
||||
return true;
|
||||
case TUPLE_VALUE_TYPE: {
|
||||
StatsDimensionsValue[] values = (StatsDimensionsValue[]) value;
|
||||
out.writeInt(values.length);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i].writeToParcel(out, flags);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
Slog.w(TAG, "readValue of an impossible type " + valueType);
|
||||
return false;
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
Slog.w(TAG, "writeValue cast failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reads mValue from a parcel. */
|
||||
private static Object readValueFromParcel(int valueType, Parcel parcel) {
|
||||
switch (valueType) {
|
||||
case STRING_VALUE_TYPE:
|
||||
return parcel.readString();
|
||||
case INT_VALUE_TYPE:
|
||||
return parcel.readInt();
|
||||
case LONG_VALUE_TYPE:
|
||||
return parcel.readLong();
|
||||
case BOOLEAN_VALUE_TYPE:
|
||||
return parcel.readBoolean();
|
||||
case FLOAT_VALUE_TYPE:
|
||||
return parcel.readFloat();
|
||||
case TUPLE_VALUE_TYPE: {
|
||||
final int sz = parcel.readInt();
|
||||
StatsDimensionsValue[] values = new StatsDimensionsValue[sz];
|
||||
for (int i = 0; i < sz; i++) {
|
||||
values[i] = new StatsDimensionsValue(parcel);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
default:
|
||||
Slog.w(TAG, "readValue of an impossible type " + valueType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,19 +17,33 @@ package android.util;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.IBinder;
|
||||
import android.os.IStatsManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* THIS ENTIRE FILE IS ONLY TEMPORARY TO PREVENT BREAKAGES OF DEPENDENCIES ON OLD APIS.
|
||||
* The new StatsManager is to be found in android.app.StatsManager.
|
||||
* TODO: Delete this file!
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* API for StatsD clients to send configurations and retrieve data.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class StatsManager {
|
||||
public class StatsManager {
|
||||
IStatsManager mService;
|
||||
private static final String TAG = "StatsManager";
|
||||
|
||||
@@ -55,7 +69,7 @@ public final class StatsManager {
|
||||
* Clients can send a configuration and simultaneously registers the name of a broadcast
|
||||
* receiver that listens for when it should request data.
|
||||
*
|
||||
* @param configKey An arbitrary string that allows clients to track the configuration.
|
||||
* @param configKey An arbitrary integer that allows clients to track the configuration.
|
||||
* @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
|
||||
* dependencies eg, conditions and matchers).
|
||||
* @param pkg The package name to receive the broadcast.
|
||||
|
||||
@@ -19,6 +19,7 @@ cc_library_shared {
|
||||
srcs: [
|
||||
":IDropBoxManagerService.aidl",
|
||||
"src/os/DropBoxManager.cpp",
|
||||
"src/os/StatsDimensionsValue.cpp",
|
||||
"src/os/StatsLogEventWrapper.cpp",
|
||||
],
|
||||
|
||||
|
||||
70
libs/services/include/android/os/StatsDimensionsValue.h
Normal file
70
libs/services/include/android/os/StatsDimensionsValue.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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_DIMENSIONS_VALUE_H
|
||||
#define STATS_DIMENSIONS_VALUE_H
|
||||
|
||||
#include <binder/Parcel.h>
|
||||
#include <binder/Parcelable.h>
|
||||
#include <binder/Status.h>
|
||||
#include <utils/String16.h>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
|
||||
// Represents a parcelable object. Used to send data from statsd to StatsCompanionService.java.
|
||||
class StatsDimensionsValue : public android::Parcelable {
|
||||
public:
|
||||
StatsDimensionsValue();
|
||||
|
||||
StatsDimensionsValue(int32_t field, String16 value);
|
||||
StatsDimensionsValue(int32_t field, int32_t value);
|
||||
StatsDimensionsValue(int32_t field, int64_t value);
|
||||
StatsDimensionsValue(int32_t field, bool value);
|
||||
StatsDimensionsValue(int32_t field, float value);
|
||||
StatsDimensionsValue(int32_t field, std::vector<StatsDimensionsValue> value);
|
||||
|
||||
virtual ~StatsDimensionsValue();
|
||||
|
||||
virtual android::status_t writeToParcel(android::Parcel* out) const override;
|
||||
virtual android::status_t readFromParcel(const android::Parcel* in) override;
|
||||
|
||||
private:
|
||||
// Keep constants in sync with android/os/StatsDimensionsValue.java
|
||||
// and stats_log.proto's DimensionValue.
|
||||
static const int kStrValueType = 2;
|
||||
static const int kIntValueType = 3;
|
||||
static const int kLongValueType = 4;
|
||||
static const int kBoolValueType = 5;
|
||||
static const int kFloatValueType = 6;
|
||||
static const int kTupleValueType = 7;
|
||||
|
||||
int32_t mField;
|
||||
int32_t mValueType;
|
||||
|
||||
// This isn't very clever, but it isn't used for long-term storage, so it'll do.
|
||||
String16 mStrValue;
|
||||
int32_t mIntValue;
|
||||
int64_t mLongValue;
|
||||
bool mBoolValue;
|
||||
float mFloatValue;
|
||||
std::vector<StatsDimensionsValue> mTupleValue;
|
||||
};
|
||||
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
|
||||
#endif // STATS_DIMENSIONS_VALUE_H
|
||||
126
libs/services/src/os/StatsDimensionsValue.cpp
Normal file
126
libs/services/src/os/StatsDimensionsValue.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "StatsDimensionsValue"
|
||||
|
||||
#include "android/os/StatsDimensionsValue.h"
|
||||
|
||||
#include <cutils/log.h>
|
||||
|
||||
using android::Parcel;
|
||||
using android::Parcelable;
|
||||
using android::status_t;
|
||||
using std::vector;
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
|
||||
StatsDimensionsValue::StatsDimensionsValue() {};
|
||||
|
||||
StatsDimensionsValue::StatsDimensionsValue(int32_t field, String16 value) :
|
||||
mField(field),
|
||||
mValueType(kStrValueType),
|
||||
mStrValue(value) {
|
||||
}
|
||||
StatsDimensionsValue::StatsDimensionsValue(int32_t field, int32_t value) :
|
||||
mField(field),
|
||||
mValueType(kIntValueType),
|
||||
mIntValue(value) {
|
||||
}
|
||||
StatsDimensionsValue::StatsDimensionsValue(int32_t field, int64_t value) :
|
||||
mField(field),
|
||||
mValueType(kLongValueType),
|
||||
mLongValue(value) {
|
||||
}
|
||||
StatsDimensionsValue::StatsDimensionsValue(int32_t field, bool value) :
|
||||
mField(field),
|
||||
mValueType(kBoolValueType),
|
||||
mBoolValue(value) {
|
||||
}
|
||||
StatsDimensionsValue::StatsDimensionsValue(int32_t field, float value) :
|
||||
mField(field),
|
||||
mValueType(kFloatValueType),
|
||||
mFloatValue(value) {
|
||||
}
|
||||
StatsDimensionsValue::StatsDimensionsValue(int32_t field, vector<StatsDimensionsValue> value) :
|
||||
mField(field),
|
||||
mValueType(kTupleValueType),
|
||||
mTupleValue(value) {
|
||||
}
|
||||
|
||||
StatsDimensionsValue::~StatsDimensionsValue() {}
|
||||
|
||||
status_t
|
||||
StatsDimensionsValue::writeToParcel(Parcel* out) const {
|
||||
status_t err ;
|
||||
|
||||
err = out->writeInt32(mField);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
err = out->writeInt32(mValueType);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
switch (mValueType) {
|
||||
case kStrValueType:
|
||||
err = out->writeString16(mStrValue);
|
||||
break;
|
||||
case kIntValueType:
|
||||
err = out->writeInt32(mIntValue);
|
||||
break;
|
||||
case kLongValueType:
|
||||
err = out->writeInt64(mLongValue);
|
||||
break;
|
||||
case kBoolValueType:
|
||||
err = out->writeBool(mBoolValue);
|
||||
break;
|
||||
case kFloatValueType:
|
||||
err = out->writeFloat(mFloatValue);
|
||||
break;
|
||||
case kTupleValueType:
|
||||
{
|
||||
int sz = mTupleValue.size();
|
||||
err = out->writeInt32(sz);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
for (int i = 0; i < sz; ++i) {
|
||||
err = mTupleValue[i].writeToParcel(out);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
err = UNKNOWN_ERROR;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t
|
||||
StatsDimensionsValue::readFromParcel(const Parcel* in)
|
||||
{
|
||||
// Implement me if desired. We don't currently use this.
|
||||
ALOGE("Cannot do c++ StatsDimensionsValue.readFromParcel(); it is not implemented.");
|
||||
(void)in; // To prevent compile error of unused parameter 'in'
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
@@ -18,16 +18,19 @@ package com.android.server.stats;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.StatsManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.net.NetworkStats;
|
||||
import android.net.wifi.IWifiManager;
|
||||
import android.net.wifi.WifiActivityEnergyInfo;
|
||||
import android.os.StatsDimensionsValue;
|
||||
import android.os.BatteryStatsInternal;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
@@ -80,9 +83,12 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
|
||||
static final String TAG = "StatsCompanionService";
|
||||
static final boolean DEBUG = true;
|
||||
|
||||
public static final String ACTION_TRIGGER_COLLECTION =
|
||||
"com.android.server.stats.action.TRIGGER_COLLECTION";
|
||||
|
||||
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
|
||||
|
||||
private final Context mContext;
|
||||
private final AlarmManager mAlarmManager;
|
||||
@GuardedBy("sStatsdLock")
|
||||
@@ -151,10 +157,37 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
|
||||
|
||||
@Override
|
||||
public void sendBroadcast(String pkg, String cls) {
|
||||
// TODO: Use a pending intent, and enfoceCallingPermission.
|
||||
mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
|
||||
UserHandle.SYSTEM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
|
||||
long subscriptionId, long subscriptionRuleId,
|
||||
StatsDimensionsValue dimensionsValue) {
|
||||
if (DEBUG) Slog.d(TAG, "Statsd requested to sendSubscriberBroadcast.");
|
||||
enforceCallingPermission();
|
||||
IntentSender intentSender = new IntentSender(intentSenderBinder);
|
||||
Intent intent = new Intent()
|
||||
.putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
|
||||
.putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
|
||||
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
|
||||
.putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
|
||||
.putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
|
||||
try {
|
||||
intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
|
||||
+ "; presumably it had been cancelled.");
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, String.format("SubscriberBroadcast params {%d %d %d %d %s}",
|
||||
configUid, configKey, subscriptionId,
|
||||
subscriptionRuleId, dimensionsValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final static int[] toIntArray(List<Integer> list) {
|
||||
int[] ret = new int[list.size()];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
|
||||
Reference in New Issue
Block a user