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:
Bookatz
2018-01-16 16:55:05 -08:00
parent 96c73a3c40
commit c697797d43
22 changed files with 1266 additions and 24 deletions

View File

@@ -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 {

View File

@@ -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

View File

@@ -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) {
}

View File

@@ -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. */

View File

@@ -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;
}

View File

@@ -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;

View 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

View 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

View File

@@ -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;

View File

@@ -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;

View 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;
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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);
}

View 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";

View 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;
}
}
}

View File

@@ -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.

View File

@@ -19,6 +19,7 @@ cc_library_shared {
srcs: [
":IDropBoxManagerService.aidl",
"src/os/DropBoxManager.cpp",
"src/os/StatsDimensionsValue.cpp",
"src/os/StatsLogEventWrapper.cpp",
],

View 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

View 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

View File

@@ -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++) {