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);
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);
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 {
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
#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>
void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
ALOGW("statscompanion service died");
mAnomalyMonitor->setStatsCompanionService(nullptr);
+ SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
}
// ======================================================================
VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor));
mAnomalyMonitor->setStatsCompanionService(statsCompanion);
+ SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
return Status::ok();
}
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 {
+ *success = false;
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ }
+}
+
+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 {
}
}
+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) {
}
*/
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. */
#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>
}
std::set<int> incidentdSections;
+
for (const Subscription& subscription : mSubscriptions) {
switch (subscription.subscriber_information_case()) {
case Subscription::SubscriberInformationCase::kIncidentdDetails:
case Subscription::SubscriberInformationCase::kPerfettoDetails:
CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
break;
+ case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
+ SubscriberReporter::getInstance()
+ .alertBroadcastSubscriber(mConfigKey, subscription, key);
+ break;
default:
break;
}
// 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;
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
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;
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;
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;
--- /dev/null
+/*
+ * 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;
+ }
+}
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;
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
package android.os;
+import android.os.StatsDimensionsValue;
import android.os.StatsLogEventWrapper;
/**
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();
}
/**
* 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.
* 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);
}
--- /dev/null
+/**
+ * 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";
\ No newline at end of file
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
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";
* 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.
srcs: [
":IDropBoxManagerService.aidl",
"src/os/DropBoxManager.cpp",
+ "src/os/StatsDimensionsValue.cpp",
"src/os/StatsLogEventWrapper.cpp",
],
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
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;
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")
@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++) {