Merge "StatsManager throws exceptions" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-04-11 17:02:06 +00:00
committed by Android (Google) Code Review
9 changed files with 290 additions and 205 deletions

View File

@@ -383,12 +383,18 @@ package android.app {
}
public final class StatsManager {
method public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException;
method public boolean addConfiguration(long, byte[]);
method public byte[] getData(long);
method public byte[] getMetadata();
method public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
method public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
method public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
method public boolean removeConfiguration(long);
method public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException;
method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
method public boolean setDataFetchOperation(long, android.app.PendingIntent);
method public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
field public static final java.lang.String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
field public static final java.lang.String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
@@ -398,6 +404,11 @@ package android.app {
field public static final java.lang.String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
}
public static class StatsManager.StatsUnavailableException extends android.util.AndroidException {
ctor public StatsManager.StatsUnavailableException(java.lang.String);
ctor public StatsManager.StatsUnavailableException(java.lang.String, java.lang.Throwable);
}
public class VrManager {
method public void setAndBindVrCompositor(android.content.ComponentName);
method public void setPersistentVrModeEnabled(boolean);

View File

@@ -780,8 +780,6 @@ Status StatsService::writeDataToDisk() {
}
void StatsService::sayHiToStatsCompanion() {
// TODO: This method needs to be private. It is temporarily public and unsecured for testing
// purposes.
sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion != nullptr) {
VLOG("Telling statsCompanion that statsd is ready");
@@ -825,42 +823,37 @@ void StatsService::OnLogEvent(LogEvent* event, bool reconnectionStarts) {
Status StatsService::getData(int64_t key, vector<uint8_t>* output) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
if (checkCallingPermission(String16(kPermissionDump))) {
ConfigKey configKey(ipc->getCallingUid(), key);
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
false /* include_current_bucket*/, output);
return Status::ok();
} else {
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
ConfigKey configKey(ipc->getCallingUid(), key);
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
false /* include_current_bucket*/, output);
return Status::ok();
}
Status StatsService::getMetadata(vector<uint8_t>* output) {
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
ipc->getCallingUid());
if (checkCallingPermission(String16(kPermissionDump))) {
StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
return Status::ok();
} else {
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
return Status::ok();
}
Status StatsService::addConfiguration(int64_t key,
const vector <uint8_t>& config,
bool* success) {
Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config) {
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
IPCThreadState* ipc = IPCThreadState::self();
if (checkCallingPermission(String16(kPermissionDump))) {
if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
*success = true;
} else {
*success = false;
}
if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
return Status::ok();
} else {
*success = false;
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
ALOGE("Could not parse malformatted StatsdConfig");
return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
"config does not correspond to a StatsdConfig proto");
}
}
@@ -876,80 +869,62 @@ bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<ui
return true;
}
Status StatsService::removeDataFetchOperation(int64_t key, bool* success) {
IPCThreadState* ipc = IPCThreadState::self();
if (checkCallingPermission(String16(kPermissionDump))) {
ConfigKey configKey(ipc->getCallingUid(), key);
mConfigManager->RemoveConfigReceiver(configKey);
*success = true;
return Status::ok();
} else {
*success = false;
Status StatsService::removeDataFetchOperation(int64_t key) {
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), key);
mConfigManager->RemoveConfigReceiver(configKey);
return Status::ok();
}
Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
bool* success) {
IPCThreadState* ipc = IPCThreadState::self();
if (checkCallingPermission(String16(kPermissionDump))) {
ConfigKey configKey(ipc->getCallingUid(), key);
mConfigManager->SetConfigReceiver(configKey, intentSender);
*success = true;
return Status::ok();
} else {
*success = false;
Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender) {
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), key);
mConfigManager->SetConfigReceiver(configKey, intentSender);
return Status::ok();
}
Status StatsService::removeConfiguration(int64_t key, bool* success) {
IPCThreadState* ipc = IPCThreadState::self();
if (checkCallingPermission(String16(kPermissionDump))) {
ConfigKey configKey(ipc->getCallingUid(), key);
mConfigManager->RemoveConfig(configKey);
SubscriberReporter::getInstance().removeConfig(configKey);
*success = true;
return Status::ok();
} else {
*success = false;
Status StatsService::removeConfiguration(int64_t key) {
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), key);
mConfigManager->RemoveConfig(configKey);
SubscriberReporter::getInstance().removeConfig(configKey);
return Status::ok();
}
Status StatsService::setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const sp<android::IBinder>& intentSender,
bool* success) {
const sp<android::IBinder>& intentSender) {
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;
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), configId);
SubscriberReporter::getInstance()
.setBroadcastSubscriber(configKey, subscriberId, intentSender);
return Status::ok();
}
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
bool* success) {
int64_t subscriberId) {
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;
if (!checkCallingPermission(String16(kPermissionDump))) {
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
}
IPCThreadState* ipc = IPCThreadState::self();
ConfigKey configKey(ipc->getCallingUid(), configId);
SubscriberReporter::getInstance()
.unsetBroadcastSubscriber(configKey, subscriberId);
return Status::ok();
}

View File

@@ -94,24 +94,23 @@ public:
* Binder call to let clients send a configuration and indicate they're interested when they
* should requestData for this configuration.
*/
virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config,
bool* success) override;
virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config) override;
/**
* Binder call to let clients register the data fetch operation for a configuration.
*/
virtual Status setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
bool* success) override;
virtual Status setDataFetchOperation(int64_t key,
const sp<android::IBinder>& intentSender) override;
/**
* Binder call to remove the data fetch operation for the specified config key.
*/
virtual Status removeDataFetchOperation(int64_t key, bool* success) override;
virtual Status removeDataFetchOperation(int64_t key) override;
/**
* Binder call to allow clients to remove the specified configuration.
*/
virtual Status removeConfiguration(int64_t key, bool* success) override;
virtual Status removeConfiguration(int64_t key) override;
/**
* Binder call to associate the given config's subscriberId with the given intentSender.
@@ -119,17 +118,13 @@ public:
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
const sp<android::IBinder>& intentSender,
bool* success) override;
const sp<android::IBinder>& intentSender) 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;
virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId) override;
// TODO: public for testing since statsd doesn't run when system starts. Change to private
// later.
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();

View File

@@ -116,11 +116,6 @@ int main(int /*argc*/, char** /*argv*/) {
ALOGE("Failed to add service");
return -1;
}
// TODO: This line is temporary, since statsd doesn't start up automatically (and therefore
// the call in StatsService::SystemRunning() won't ever be called right now).
// TODO: Are you sure? Don't we need to reconnect to the system process if we get restarted?
// --joeo
service->sayHiToStatsCompanion();
// Start the log reader thread

View File

@@ -35,7 +35,7 @@ void SendConfig(StatsService& service, const StatsdConfig& config) {
config.SerializeToString(&str);
std::vector<uint8_t> configAsVec(str.begin(), str.end());
bool success;
service.addConfiguration(kConfigKey, configAsVec, &success);
service.addConfiguration(kConfigKey, configAsVec);
}
ConfigMetricsReport GetReports(StatsService& service) {

View File

@@ -19,6 +19,7 @@ import android.app.Activity;
import android.app.PendingIntent;
import android.app.IntentService;
import android.app.StatsManager;
import android.app.StatsManager.StatsUnavailableException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -171,12 +172,16 @@ public class MainActivity extends Activity {
return;
}
if (mStatsManager != null) {
byte[] data = mStatsManager.getData(CONFIG_ID);
if (data != null) {
displayData(data);
} else {
mReportText.setText("Failed!");
try {
byte[] data = mStatsManager.getReports(CONFIG_ID);
if (data != null) {
displayData(data);
return;
}
} catch (StatsUnavailableException e) {
Log.e(TAG, "Failed to get data from statsd", e);
}
mReportText.setText("Failed!");
}
}
});
@@ -194,10 +199,11 @@ public class MainActivity extends Activity {
byte[] config = new byte[inputStream.available()];
inputStream.read(config);
if (mStatsManager != null) {
if (mStatsManager.addConfiguration(CONFIG_ID, config)) {
try {
mStatsManager.addConfig(CONFIG_ID, config);
Toast.makeText(
MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
} else {
} catch (StatsUnavailableException | IllegalArgumentException e) {
Toast.makeText(MainActivity.this, "Config push FAILED!",
Toast.LENGTH_LONG).show();
}
@@ -218,11 +224,12 @@ public class MainActivity extends Activity {
return;
}
if (mStatsManager != null) {
if (mStatsManager.setDataFetchOperation(CONFIG_ID, pi)) {
try {
mStatsManager.setFetchReportsOperation(pi, CONFIG_ID);
Toast.makeText(MainActivity.this,
"Receiver specified to pending intent", Toast.LENGTH_LONG)
.show();
} else {
} catch (StatsUnavailableException e) {
Toast.makeText(MainActivity.this, "Statsd did not set receiver",
Toast.LENGTH_LONG)
.show();
@@ -241,10 +248,11 @@ public class MainActivity extends Activity {
return;
}
if (mStatsManager != null) {
if (mStatsManager.setDataFetchOperation(CONFIG_ID, null)) {
try {
mStatsManager.setFetchReportsOperation(null, CONFIG_ID);
Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG)
.show();
} else {
} catch (StatsUnavailableException e) {
Toast.makeText(MainActivity.this, "Statsd did not remove receiver",
Toast.LENGTH_LONG)
.show();

View File

@@ -355,7 +355,13 @@ public class LoadtestActivity extends Activity implements AdapterView.OnItemSele
return null;
}
if (mStatsManager != null) {
byte[] data = mStatsManager.getMetadata();
byte[] data;
try {
data = mStatsManager.getStatsMetadata();
} catch (StatsManager.StatsUnavailableException e) {
Log.e(TAG, "Failed to get data from statsd", e);
return null;
}
if (data != null) {
StatsdStatsReport report = null;
boolean good = false;
@@ -375,7 +381,13 @@ public class LoadtestActivity extends Activity implements AdapterView.OnItemSele
return null;
}
if (mStatsManager != null) {
byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_ID);
byte[] data;
try {
data = mStatsManager.getReports(ConfigFactory.CONFIG_ID);
} catch (StatsManager.StatsUnavailableException e) {
Log.e(TAG, "Failed to get data from statsd", e);
return null;
}
if (data != null) {
ConfigMetricsReportList reports = null;
try {
@@ -563,10 +575,11 @@ public class LoadtestActivity extends Activity implements AdapterView.OnItemSele
// TODO: Clear all configs instead of specific ones.
if (mStatsManager != null) {
if (mStarted) {
if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_ID)) {
try {
mStatsManager.removeConfig(ConfigFactory.CONFIG_ID);
Log.d(TAG, "Removed loadtest statsd configs.");
} else {
Log.d(TAG, "Failed to remove loadtest configs.");
} catch (StatsManager.StatsUnavailableException e) {
Log.e(TAG, "Failed to remove loadtest configs.", e);
}
}
}
@@ -574,12 +587,13 @@ public class LoadtestActivity extends Activity implements AdapterView.OnItemSele
private boolean setConfig(ConfigFactory.ConfigMetadata configData) {
if (mStatsManager != null) {
if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID, configData.bytes)) {
try {
mStatsManager.addConfig(ConfigFactory.CONFIG_ID, configData.bytes);
mNumMetrics = configData.numMetrics;
Log.d(TAG, "Config pushed to statsd");
return true;
} else {
Log.d(TAG, "Failed to push config to statsd");
} catch (StatsManager.StatsUnavailableException | IllegalArgumentException e) {
Log.e(TAG, "Failed to push config to statsd", e);
}
}
return false;

View File

@@ -23,6 +23,7 @@ import android.os.IBinder;
import android.os.IStatsManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.AndroidException;
import android.util.Slog;
/**
@@ -82,54 +83,73 @@ 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.
* Adds the given configuration and associates it with the given configKey. If a config with the
* given configKey already exists for the caller's uid, it is replaced with the new one.
*
* @param configKey An arbitrary integer that allows clients to track the configuration.
* @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
* @param config Wire-encoded StatsdConfig proto that specifies metrics (and all
* dependencies eg, conditions and matchers).
* @return true if successful
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
* @throws IllegalArgumentException if config is not a wire-encoded StatsdConfig proto
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean addConfiguration(long configKey, byte[] config) {
public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
Slog.e(TAG, "Failed to find statsd when adding configuration");
return false;
}
return service.addConfiguration(configKey, config);
service.addConfiguration(configKey, config); // can throw IllegalArgumentException
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when adding configuration");
return false;
throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
* TODO: Temporary for backwards compatibility. Remove.
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean addConfiguration(long configKey, byte[] config) {
try {
addConfig(configKey, config);
return true;
} catch (StatsUnavailableException | IllegalArgumentException e) {
return false;
}
}
/**
* Remove a configuration from logging.
*
* @param configKey Configuration key to remove.
* @return true if successful
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean removeConfiguration(long configKey) {
public void removeConfig(long configKey) throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
Slog.e(TAG, "Failed to find statsd when removing configuration");
return false;
}
return service.removeConfiguration(configKey);
service.removeConfiguration(configKey);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when removing configuration");
return false;
throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
* TODO: Temporary for backwards compatibility. Remove.
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean removeConfiguration(long configKey) {
try {
removeConfig(configKey);
return true;
} catch (StatsUnavailableException e) {
return false;
}
}
/**
* Set the PendingIntent to be used when broadcasting subscriber information to the given
* subscriberId within the given config.
@@ -150,123 +170,165 @@ public final class StatsManager {
* {@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()).
* time statsd starts. The config must have been added first (via {@link #addConfig}).
*
* @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
* @param configKey The integer naming the config to which this subscriber is attached.
* @param subscriberId ID of the subscriber, as used in the config.
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*/
@RequiresPermission(Manifest.permission.DUMP)
public void setBroadcastSubscriber(
PendingIntent pendingIntent, long configKey, long subscriberId)
throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (pendingIntent != null) {
// Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
IBinder intentSender = pendingIntent.getTarget().asBinder();
service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
} else {
service.unsetBroadcastSubscriber(configKey, subscriberId);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
* TODO: Temporary for backwards compatibility. Remove.
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean setBroadcastSubscriber(
long configKey, long subscriberId, PendingIntent pendingIntent) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
Slog.e(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.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
return false;
}
try {
setBroadcastSubscriber(pendingIntent, configKey, subscriberId);
return true;
} catch (StatsUnavailableException e) {
return false;
}
}
/**
* Registers the operation that is called to retrieve the metrics data. This must be called
* each time statsd starts. The config must have been added first (via addConfiguration(),
* although addConfiguration could have been called on a previous boot). This operation allows
* each time statsd starts. The config must have been added first (via {@link #addConfig},
* although addConfig could have been called on a previous boot). This operation allows
* statsd to send metrics data whenever statsd determines that the metrics in memory are
* approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
* data, which also deletes the retrieved metrics from statsd's memory.
* approaching the memory limits. The fetch operation should call {@link #getReports} to fetch
* the data, which also deletes the retrieved metrics from statsd's memory.
*
* @param configKey The integer naming the config to which this operation is attached.
* @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
* associated with the given subscriberId. May be null, in which case
* it removes any associated pending intent with this configKey.
* @return true if successful
* @param configKey The integer naming the config to which this operation is attached.
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*/
@RequiresPermission(Manifest.permission.DUMP)
public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey)
throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
Slog.e(TAG, "Failed to find statsd when registering data listener.");
return false;
}
if (pendingIntent == null) {
return service.removeDataFetchOperation(configKey);
service.removeDataFetchOperation(configKey);
} else {
// Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
IBinder intentSender = pendingIntent.getTarget().asBinder();
return service.setDataFetchOperation(configKey, intentSender);
service.setDataFetchOperation(configKey, intentSender);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
return false;
throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
* 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 (eg, if statsd
* crashed).
* TODO: Temporary for backwards compatibility. Remove.
*/
@RequiresPermission(Manifest.permission.DUMP)
public @Nullable byte[] getData(long configKey) {
public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
try {
setFetchReportsOperation(pendingIntent, configKey);
return true;
} catch (StatsUnavailableException e) {
return false;
}
}
/**
* Request the data collected for the given configKey.
* This getter is destructive - it also clears the retrieved metrics from statsd's memory.
*
* @param configKey Configuration key to retrieve data from.
* @return Serialized ConfigMetricsReportList proto.
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*/
@RequiresPermission(Manifest.permission.DUMP)
public byte[] getReports(long configKey) throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
Slog.e(TAG, "Failed to find statsd when getting data");
return null;
}
return service.getData(configKey);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when getting data");
return null;
throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
* TODO: Temporary for backwards compatibility. Remove.
*/
@RequiresPermission(Manifest.permission.DUMP)
public @Nullable byte[] getData(long configKey) {
try {
return getReports(configKey);
} catch (StatsUnavailableException e) {
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 #getReports(long)}.
* This getter is not destructive and will not reset any metrics/counters.
*
* @return Serialized StatsdStatsReport proto.
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
*/
@RequiresPermission(Manifest.permission.DUMP)
public byte[] getStatsMetadata() throws StatsUnavailableException {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
return service.getMetadata();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when getting metadata");
throw new StatsUnavailableException("could not connect", e);
}
}
}
/**
* 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)}.
* the actual metrics themselves (metrics must be collected via {@link #getReports(long)}.
* This getter is not destructive and will not reset any metrics/counters.
*
* @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
*/
@RequiresPermission(Manifest.permission.DUMP)
public @Nullable byte[] getMetadata() {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
Slog.e(TAG, "Failed to find statsd when getting metadata");
return null;
}
return service.getMetadata();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to connect to statsd when getting metadata");
return null;
}
try {
return getStatsMetadata();
} catch (StatsUnavailableException e) {
return null;
}
}
@@ -279,14 +341,33 @@ public final class StatsManager {
}
}
private IStatsManager getIStatsManagerLocked() throws RemoteException {
private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
if (mService != null) {
return mService;
}
mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
if (mService != null) {
if (mService == null) {
throw new StatsUnavailableException("could not be found");
}
try {
mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
} catch (RemoteException e) {
throw new StatsUnavailableException("could not connect when linkToDeath", e);
}
return mService;
}
/**
* Exception thrown when communication with the stats service fails (eg if it is not available).
* This might be thrown early during boot before the stats service has started or if it crashed.
*/
public static class StatsUnavailableException extends AndroidException {
public StatsUnavailableException(String reason) {
super("Failed to connect to statsd: " + reason);
}
public StatsUnavailableException(String reason, Throwable e) {
super("Failed to connect to statsd: " + reason, e);
}
}
}

View File

@@ -77,45 +77,51 @@ interface IStatsManager {
/**
* Fetches data for the specified configuration key. Returns a byte array representing proto
* wire-encoded of ConfigMetricsReportList.
*
* Requires Manifest.permission.DUMP.
*/
byte[] getData(in long key);
/**
* Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
*
* Requires Manifest.permission.DUMP.
*/
byte[] getMetadata();
/**
* 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 receiver for this data is
* The configuration must be a wire-encoded StatsdConfig. The receiver for this data is
* registered in a separate function.
*
* Returns if this configuration was correctly registered.
* Requires Manifest.permission.DUMP.
*/
boolean addConfiguration(in long configKey, in byte[] config);
void addConfiguration(in long configKey, in byte[] config);
/**
* Registers the given pending intent for this config key. This intent is invoked when the
* memory consumed by the metrics for this configuration approach the pre-defined limits. There
* can be at most one listener per config key.
*
* Returns if this listener was correctly registered.
* Requires Manifest.permission.DUMP.
*/
boolean setDataFetchOperation(long configKey, in IBinder intentSender);
void setDataFetchOperation(long configKey, in IBinder intentSender);
/**
* Removes the data fetch operation for the specified configuration.
*
* Requires Manifest.permission.DUMP.
*/
boolean removeDataFetchOperation(long configKey);
void removeDataFetchOperation(long configKey);
/**
* Removes the configuration with the matching config key. No-op if this config key does not
* exist.
*
* Returns if this configuration key was removed.
* Requires Manifest.permission.DUMP.
*/
boolean removeConfiguration(in long configKey);
void removeConfiguration(in long configKey);
/**
* Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
@@ -133,16 +139,16 @@ interface IStatsManager {
* intentSender must be convertible into an IntentSender using IntentSender(IBinder)
* and cannot be null.
*
* Returns true if successful.
* Requires Manifest.permission.DUMP.
*/
boolean setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
void 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.
* Requires Manifest.permission.DUMP.
*/
boolean unsetBroadcastSubscriber(long configKey, long subscriberId);
void unsetBroadcastSubscriber(long configKey, long subscriberId);
}