Merge "Protect usage data with OP_GET_USAGE_STATS." into pi-dev
am: 936c0868f8
Change-Id: I3c84c646b3a79f09dc1210e18f48d1f87d38abd4
This commit is contained in:
@@ -27,8 +27,10 @@
|
||||
#include "subscriber/SubscriberReporter.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/PermissionController.h>
|
||||
#include <dirent.h>
|
||||
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
@@ -42,13 +44,83 @@
|
||||
|
||||
using namespace android;
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
namespace android {
|
||||
namespace os {
|
||||
namespace statsd {
|
||||
|
||||
constexpr const char* kPermissionDump = "android.permission.DUMP";
|
||||
constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
|
||||
|
||||
constexpr const char* kOpUsage = "android:get_usage_stats";
|
||||
|
||||
#define STATS_SERVICE_DIR "/data/misc/stats-service"
|
||||
|
||||
static binder::Status ok() {
|
||||
return binder::Status::ok();
|
||||
}
|
||||
|
||||
static binder::Status exception(uint32_t code, const std::string& msg) {
|
||||
ALOGE("%s (%d)", msg.c_str(), code);
|
||||
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
|
||||
}
|
||||
|
||||
binder::Status checkUid(uid_t expectedUid) {
|
||||
uid_t uid = IPCThreadState::self()->getCallingUid();
|
||||
if (uid == expectedUid || uid == AID_ROOT) {
|
||||
return ok();
|
||||
} else {
|
||||
return exception(binder::Status::EX_SECURITY,
|
||||
StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
|
||||
}
|
||||
}
|
||||
|
||||
binder::Status checkDumpAndUsageStats(const String16& packageName) {
|
||||
pid_t pid = IPCThreadState::self()->getCallingPid();
|
||||
uid_t uid = IPCThreadState::self()->getCallingUid();
|
||||
|
||||
// Root, system, and shell always have access
|
||||
if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
|
||||
return ok();
|
||||
}
|
||||
|
||||
// Caller must be granted these permissions
|
||||
if (!checkCallingPermission(String16(kPermissionDump))) {
|
||||
return exception(binder::Status::EX_SECURITY,
|
||||
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
|
||||
}
|
||||
if (!checkCallingPermission(String16(kPermissionUsage))) {
|
||||
return exception(binder::Status::EX_SECURITY,
|
||||
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
|
||||
}
|
||||
|
||||
// Caller must also have usage stats op granted
|
||||
PermissionController pc;
|
||||
switch (pc.noteOp(String16(kOpUsage), uid, packageName)) {
|
||||
case PermissionController::MODE_ALLOWED:
|
||||
case PermissionController::MODE_DEFAULT:
|
||||
return ok();
|
||||
default:
|
||||
return exception(binder::Status::EX_SECURITY,
|
||||
StringPrintf("UID %d / PID %d lacks app-op %s", uid, pid, kOpUsage));
|
||||
}
|
||||
}
|
||||
|
||||
#define ENFORCE_UID(uid) { \
|
||||
binder::Status status = checkUid((uid)); \
|
||||
if (!status.isOk()) { \
|
||||
return status; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ENFORCE_DUMP_AND_USAGE_STATS(packageName) { \
|
||||
binder::Status status = checkDumpAndUsageStats(packageName); \
|
||||
if (!status.isOk()) { \
|
||||
return status; \
|
||||
} \
|
||||
}
|
||||
|
||||
StatsService::StatsService(const sp<Looper>& handlerLooper)
|
||||
: mAnomalyAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
|
||||
[](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
|
||||
@@ -212,7 +284,10 @@ void StatsService::dump_impl(FILE* out, bool verbose, bool proto) {
|
||||
* Implementation of the adb shell cmd stats command.
|
||||
*/
|
||||
status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
|
||||
// TODO: Permission check
|
||||
uid_t uid = IPCThreadState::self()->getCallingUid();
|
||||
if (uid != AID_ROOT && uid != AID_SHELL) {
|
||||
return PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
const int argCount = args.size();
|
||||
if (argCount >= 1) {
|
||||
@@ -661,13 +736,9 @@ status_t StatsService::cmd_clear_puller_cache(FILE* out) {
|
||||
|
||||
Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
|
||||
const vector<String16>& app) {
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
VLOG("StatsService::informAllUidData was called");
|
||||
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call informAllUidData");
|
||||
}
|
||||
|
||||
mUidMap->updateMap(getElapsedRealtimeNs(), uid, version, app);
|
||||
VLOG("StatsService::informAllUidData succeeded");
|
||||
|
||||
@@ -675,36 +746,26 @@ Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<i
|
||||
}
|
||||
|
||||
Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version) {
|
||||
VLOG("StatsService::informOnePackage was called");
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call informOnePackage");
|
||||
}
|
||||
VLOG("StatsService::informOnePackage was called");
|
||||
mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version);
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
|
||||
VLOG("StatsService::informOnePackageRemoved was called");
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call informOnePackageRemoved");
|
||||
}
|
||||
VLOG("StatsService::informOnePackageRemoved was called");
|
||||
mUidMap->removeApp(getElapsedRealtimeNs(), app, uid);
|
||||
mConfigManager->RemoveConfigs(uid);
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status StatsService::informAnomalyAlarmFired() {
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
VLOG("StatsService::informAnomalyAlarmFired was called");
|
||||
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call informAnomalyAlarmFired");
|
||||
}
|
||||
|
||||
int64_t currentTimeSec = getElapsedRealtimeSec();
|
||||
std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
|
||||
mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
|
||||
@@ -718,14 +779,9 @@ Status StatsService::informAnomalyAlarmFired() {
|
||||
}
|
||||
|
||||
Status StatsService::informAlarmForSubscriberTriggeringFired() {
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called");
|
||||
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(
|
||||
Status::EX_SECURITY,
|
||||
"Only system uid can call informAlarmForSubscriberTriggeringFired");
|
||||
}
|
||||
|
||||
int64_t currentTimeSec = getElapsedRealtimeSec();
|
||||
std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
|
||||
mPeriodicAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
|
||||
@@ -739,44 +795,28 @@ Status StatsService::informAlarmForSubscriberTriggeringFired() {
|
||||
}
|
||||
|
||||
Status StatsService::informPollAlarmFired() {
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
VLOG("StatsService::informPollAlarmFired was called");
|
||||
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call informPollAlarmFired");
|
||||
}
|
||||
|
||||
mProcessor->informPullAlarmFired(getElapsedRealtimeNs());
|
||||
|
||||
VLOG("StatsService::informPollAlarmFired succeeded");
|
||||
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status StatsService::systemRunning() {
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call systemRunning");
|
||||
}
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
// When system_server is up and running, schedule the dropbox task to run.
|
||||
VLOG("StatsService::systemRunning");
|
||||
|
||||
sayHiToStatsCompanion();
|
||||
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status StatsService::writeDataToDisk() {
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call systemRunning");
|
||||
}
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
VLOG("StatsService::writeDataToDisk");
|
||||
|
||||
mProcessor->WriteDataToDisk();
|
||||
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
@@ -791,13 +831,9 @@ void StatsService::sayHiToStatsCompanion() {
|
||||
}
|
||||
|
||||
Status StatsService::statsCompanionReady() {
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
|
||||
VLOG("StatsService::statsCompanionReady was called");
|
||||
|
||||
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
|
||||
return Status::fromExceptionCode(Status::EX_SECURITY,
|
||||
"Only system uid can call statsCompanionReady");
|
||||
}
|
||||
|
||||
sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
|
||||
if (statsCompanion == nullptr) {
|
||||
return Status::fromExceptionCode(
|
||||
@@ -821,33 +857,31 @@ void StatsService::OnLogEvent(LogEvent* event, bool reconnectionStarts) {
|
||||
mProcessor->OnLogEvent(event, reconnectionStarts);
|
||||
}
|
||||
|
||||
Status StatsService::getData(int64_t key, vector<uint8_t>* output) {
|
||||
Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
|
||||
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) {
|
||||
Status StatsService::getMetadata(const String16& packageName, vector<uint8_t>* output) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
|
||||
ipc->getCallingUid());
|
||||
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) {
|
||||
if (!checkCallingPermission(String16(kPermissionDump))) {
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
|
||||
const String16& packageName) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
|
||||
return Status::ok();
|
||||
@@ -870,30 +904,29 @@ bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<ui
|
||||
return true;
|
||||
}
|
||||
|
||||
Status StatsService::removeDataFetchOperation(int64_t key) {
|
||||
if (!checkCallingPermission(String16(kPermissionDump))) {
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
Status StatsService::removeDataFetchOperation(int64_t key, const String16& packageName) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
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) {
|
||||
if (!checkCallingPermission(String16(kPermissionDump))) {
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
Status StatsService::setDataFetchOperation(int64_t key,
|
||||
const sp<android::IBinder>& intentSender,
|
||||
const String16& packageName) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
ConfigKey configKey(ipc->getCallingUid(), key);
|
||||
mConfigManager->SetConfigReceiver(configKey, intentSender);
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
Status StatsService::removeConfiguration(int64_t key) {
|
||||
if (!checkCallingPermission(String16(kPermissionDump))) {
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
Status StatsService::removeConfiguration(int64_t key, const String16& packageName) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
ConfigKey configKey(ipc->getCallingUid(), key);
|
||||
mConfigManager->RemoveConfig(configKey);
|
||||
@@ -903,11 +936,11 @@ Status StatsService::removeConfiguration(int64_t key) {
|
||||
|
||||
Status StatsService::setBroadcastSubscriber(int64_t configId,
|
||||
int64_t subscriberId,
|
||||
const sp<android::IBinder>& intentSender) {
|
||||
const sp<android::IBinder>& intentSender,
|
||||
const String16& packageName) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
VLOG("StatsService::setBroadcastSubscriber called.");
|
||||
if (!checkCallingPermission(String16(kPermissionDump))) {
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
ConfigKey configKey(ipc->getCallingUid(), configId);
|
||||
SubscriberReporter::getInstance()
|
||||
@@ -916,11 +949,11 @@ Status StatsService::setBroadcastSubscriber(int64_t configId,
|
||||
}
|
||||
|
||||
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
|
||||
int64_t subscriberId) {
|
||||
int64_t subscriberId,
|
||||
const String16& packageName) {
|
||||
ENFORCE_DUMP_AND_USAGE_STATS(packageName);
|
||||
|
||||
VLOG("StatsService::unsetBroadcastSubscriber called.");
|
||||
if (!checkCallingPermission(String16(kPermissionDump))) {
|
||||
return Status::fromExceptionCode(binder::Status::EX_SECURITY);
|
||||
}
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
ConfigKey configKey(ipc->getCallingUid(), configId);
|
||||
SubscriberReporter::getInstance()
|
||||
@@ -928,7 +961,6 @@ Status StatsService::unsetBroadcastSubscriber(int64_t configId,
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
|
||||
void StatsService::binderDied(const wp <IBinder>& who) {
|
||||
ALOGW("statscompanion service died");
|
||||
mProcessor->WriteDataToDisk();
|
||||
|
||||
@@ -81,36 +81,44 @@ public:
|
||||
/**
|
||||
* Binder call for clients to request data for this configuration key.
|
||||
*/
|
||||
virtual Status getData(int64_t key, vector<uint8_t>* output) override;
|
||||
virtual Status getData(int64_t key,
|
||||
const String16& packageName,
|
||||
vector<uint8_t>* output) override;
|
||||
|
||||
|
||||
/**
|
||||
* Binder call for clients to get metadata across all configs in statsd.
|
||||
*/
|
||||
virtual Status getMetadata(vector<uint8_t>* output) override;
|
||||
virtual Status getMetadata(const String16& packageName,
|
||||
vector<uint8_t>* output) override;
|
||||
|
||||
|
||||
/**
|
||||
* 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) override;
|
||||
virtual Status addConfiguration(int64_t key,
|
||||
const vector<uint8_t>& config,
|
||||
const String16& packageName) 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) override;
|
||||
const sp<android::IBinder>& intentSender,
|
||||
const String16& packageName) override;
|
||||
|
||||
/**
|
||||
* Binder call to remove the data fetch operation for the specified config key.
|
||||
*/
|
||||
virtual Status removeDataFetchOperation(int64_t key) override;
|
||||
virtual Status removeDataFetchOperation(int64_t key,
|
||||
const String16& packageName) override;
|
||||
|
||||
/**
|
||||
* Binder call to allow clients to remove the specified configuration.
|
||||
*/
|
||||
virtual Status removeConfiguration(int64_t key) override;
|
||||
virtual Status removeConfiguration(int64_t key,
|
||||
const String16& packageName) override;
|
||||
|
||||
/**
|
||||
* Binder call to associate the given config's subscriberId with the given intentSender.
|
||||
@@ -118,12 +126,15 @@ public:
|
||||
*/
|
||||
virtual Status setBroadcastSubscriber(int64_t configId,
|
||||
int64_t subscriberId,
|
||||
const sp<android::IBinder>& intentSender) override;
|
||||
const sp<android::IBinder>& intentSender,
|
||||
const String16& packageName) override;
|
||||
|
||||
/**
|
||||
* Binder call to unassociate the given config's subscriberId with any intentSender.
|
||||
*/
|
||||
virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId) override;
|
||||
virtual Status unsetBroadcastSubscriber(int64_t configId,
|
||||
int64_t subscriberId,
|
||||
const String16& packageName) override;
|
||||
|
||||
/** Inform statsCompanion that statsd is ready. */
|
||||
virtual void sayHiToStatsCompanion();
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace statsd {
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
const string kAndroid = "android";
|
||||
const string kApp1 = "app1.sharing.1";
|
||||
const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs.
|
||||
|
||||
@@ -35,12 +36,12 @@ 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);
|
||||
service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str()));
|
||||
}
|
||||
|
||||
ConfigMetricsReport GetReports(StatsService& service) {
|
||||
vector<uint8_t> output;
|
||||
service.getData(kConfigKey, &output);
|
||||
service.getData(kConfigKey, String16(kAndroid.c_str()), &output);
|
||||
ConfigMetricsReportList reports;
|
||||
reports.ParseFromArray(output.data(), output.size());
|
||||
EXPECT_EQ(1, reports.reports_size());
|
||||
@@ -134,4 +135,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
} // namespace android
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
import android.Manifest;
|
||||
import static android.Manifest.permission.DUMP;
|
||||
import static android.Manifest.permission.PACKAGE_USAGE_STATS;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.IStatsManager;
|
||||
import android.os.RemoteException;
|
||||
@@ -33,10 +36,13 @@ import android.util.Slog;
|
||||
*/
|
||||
@SystemApi
|
||||
public final class StatsManager {
|
||||
IStatsManager mService;
|
||||
private static final String TAG = "StatsManager";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private IStatsManager mService;
|
||||
|
||||
/**
|
||||
* Long extra of uid that added the relevant stats config.
|
||||
*/
|
||||
@@ -79,7 +85,8 @@ public final class StatsManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public StatsManager() {
|
||||
public StatsManager(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,15 +99,18 @@ public final class StatsManager {
|
||||
* @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)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
service.addConfiguration(configKey, config); // can throw IllegalArgumentException
|
||||
// can throw IllegalArgumentException
|
||||
service.addConfiguration(configKey, config, mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to connect to statsd when adding configuration");
|
||||
throw new StatsUnavailableException("could not connect", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new StatsUnavailableException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +118,7 @@ public final class StatsManager {
|
||||
/**
|
||||
* TODO: Temporary for backwards compatibility. Remove.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public boolean addConfiguration(long configKey, byte[] config) {
|
||||
try {
|
||||
addConfig(configKey, config);
|
||||
@@ -124,15 +134,17 @@ public final class StatsManager {
|
||||
* @param configKey Configuration key to remove.
|
||||
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public void removeConfig(long configKey) throws StatsUnavailableException {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
service.removeConfiguration(configKey);
|
||||
service.removeConfiguration(configKey, mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to connect to statsd when removing configuration");
|
||||
throw new StatsUnavailableException("could not connect", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new StatsUnavailableException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +152,7 @@ public final class StatsManager {
|
||||
/**
|
||||
* TODO: Temporary for backwards compatibility. Remove.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public boolean removeConfiguration(long configKey) {
|
||||
try {
|
||||
removeConfig(configKey);
|
||||
@@ -179,7 +191,7 @@ public final class StatsManager {
|
||||
* @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)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public void setBroadcastSubscriber(
|
||||
PendingIntent pendingIntent, long configKey, long subscriberId)
|
||||
throws StatsUnavailableException {
|
||||
@@ -189,13 +201,17 @@ public final class StatsManager {
|
||||
if (pendingIntent != null) {
|
||||
// Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
|
||||
IBinder intentSender = pendingIntent.getTarget().asBinder();
|
||||
service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
|
||||
service.setBroadcastSubscriber(configKey, subscriberId, intentSender,
|
||||
mContext.getOpPackageName());
|
||||
} else {
|
||||
service.unsetBroadcastSubscriber(configKey, subscriberId);
|
||||
service.unsetBroadcastSubscriber(configKey, subscriberId,
|
||||
mContext.getOpPackageName());
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
|
||||
throw new StatsUnavailableException("could not connect", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new StatsUnavailableException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +219,7 @@ public final class StatsManager {
|
||||
/**
|
||||
* TODO: Temporary for backwards compatibility. Remove.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public boolean setBroadcastSubscriber(
|
||||
long configKey, long subscriberId, PendingIntent pendingIntent) {
|
||||
try {
|
||||
@@ -228,23 +244,26 @@ public final class StatsManager {
|
||||
* @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)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey)
|
||||
throws StatsUnavailableException {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
if (pendingIntent == null) {
|
||||
service.removeDataFetchOperation(configKey);
|
||||
service.removeDataFetchOperation(configKey, mContext.getOpPackageName());
|
||||
} else {
|
||||
// Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
|
||||
IBinder intentSender = pendingIntent.getTarget().asBinder();
|
||||
service.setDataFetchOperation(configKey, intentSender);
|
||||
service.setDataFetchOperation(configKey, intentSender,
|
||||
mContext.getOpPackageName());
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
|
||||
throw new StatsUnavailableException("could not connect", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new StatsUnavailableException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,7 +271,7 @@ public final class StatsManager {
|
||||
/**
|
||||
* TODO: Temporary for backwards compatibility. Remove.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
|
||||
try {
|
||||
setFetchReportsOperation(pendingIntent, configKey);
|
||||
@@ -270,15 +289,17 @@ public final class StatsManager {
|
||||
* @return Serialized ConfigMetricsReportList proto.
|
||||
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public byte[] getReports(long configKey) throws StatsUnavailableException {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
return service.getData(configKey);
|
||||
return service.getData(configKey, mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to connect to statsd when getting data");
|
||||
throw new StatsUnavailableException("could not connect", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new StatsUnavailableException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,7 +307,7 @@ public final class StatsManager {
|
||||
/**
|
||||
* TODO: Temporary for backwards compatibility. Remove.
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public @Nullable byte[] getData(long configKey) {
|
||||
try {
|
||||
return getReports(configKey);
|
||||
@@ -303,15 +324,17 @@ public final class StatsManager {
|
||||
* @return Serialized StatsdStatsReport proto.
|
||||
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public byte[] getStatsMetadata() throws StatsUnavailableException {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
return service.getMetadata();
|
||||
return service.getMetadata(mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to connect to statsd when getting metadata");
|
||||
throw new StatsUnavailableException("could not connect", e);
|
||||
} catch (SecurityException e) {
|
||||
throw new StatsUnavailableException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,7 +346,7 @@ public final class StatsManager {
|
||||
*
|
||||
* @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.DUMP)
|
||||
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
|
||||
public @Nullable byte[] getMetadata() {
|
||||
try {
|
||||
return getStatsMetadata();
|
||||
|
||||
@@ -458,11 +458,11 @@ final class SystemServiceRegistry {
|
||||
}});
|
||||
|
||||
registerService(Context.STATS_MANAGER, StatsManager.class,
|
||||
new StaticServiceFetcher<StatsManager>() {
|
||||
@Override
|
||||
public StatsManager createService() throws ServiceNotFoundException {
|
||||
return new StatsManager();
|
||||
}});
|
||||
new CachedServiceFetcher<StatsManager>() {
|
||||
@Override
|
||||
public StatsManager createService(ContextImpl ctx) {
|
||||
return new StatsManager(ctx.getOuterContext());
|
||||
}});
|
||||
|
||||
registerService(Context.STATUS_BAR_SERVICE, StatusBarManager.class,
|
||||
new CachedServiceFetcher<StatusBarManager>() {
|
||||
|
||||
@@ -20,6 +20,7 @@ package android.os;
|
||||
/** @hide */
|
||||
interface IPermissionController {
|
||||
boolean checkPermission(String permission, int pid, int uid);
|
||||
int noteOp(String op, int uid, String packageName);
|
||||
String[] getPackagesForUid(int uid);
|
||||
boolean isRuntimePermission(String permission);
|
||||
int getPackageUid(String packageName, int flags);
|
||||
|
||||
@@ -80,14 +80,14 @@ interface IStatsManager {
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
byte[] getData(in long key);
|
||||
byte[] getData(in long key, in String packageName);
|
||||
|
||||
/**
|
||||
* Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
byte[] getMetadata();
|
||||
byte[] getMetadata(in String packageName);
|
||||
|
||||
/**
|
||||
* Sets a configuration with the specified config key and subscribes to updates for this
|
||||
@@ -97,7 +97,7 @@ interface IStatsManager {
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
void addConfiguration(in long configKey, in byte[] config);
|
||||
void addConfiguration(in long configKey, in byte[] config, in String packageName);
|
||||
|
||||
/**
|
||||
* Registers the given pending intent for this config key. This intent is invoked when the
|
||||
@@ -106,14 +106,14 @@ interface IStatsManager {
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
void setDataFetchOperation(long configKey, in IBinder intentSender);
|
||||
void setDataFetchOperation(long configKey, in IBinder intentSender, in String packageName);
|
||||
|
||||
/**
|
||||
* Removes the data fetch operation for the specified configuration.
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
void removeDataFetchOperation(long configKey);
|
||||
void removeDataFetchOperation(long configKey, in String packageName);
|
||||
|
||||
/**
|
||||
* Removes the configuration with the matching config key. No-op if this config key does not
|
||||
@@ -121,7 +121,7 @@ interface IStatsManager {
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
void removeConfiguration(in long configKey);
|
||||
void removeConfiguration(in long configKey, in String packageName);
|
||||
|
||||
/**
|
||||
* Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
|
||||
@@ -141,7 +141,8 @@ interface IStatsManager {
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
void setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
|
||||
void setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender,
|
||||
in String packageName);
|
||||
|
||||
/**
|
||||
* Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
|
||||
@@ -150,5 +151,5 @@ interface IStatsManager {
|
||||
*
|
||||
* Requires Manifest.permission.DUMP.
|
||||
*/
|
||||
void unsetBroadcastSubscriber(long configKey, long subscriberId);
|
||||
void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
|
||||
}
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
<assign-permission name="android.permission.DUMP" uid="statsd" />
|
||||
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
|
||||
<assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
|
||||
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="statsd" />
|
||||
|
||||
<!-- This is a list of all the libraries available for application
|
||||
code to link against. -->
|
||||
|
||||
@@ -8860,6 +8860,12 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
uid) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int noteOp(String op, int uid, String packageName) {
|
||||
return mActivityManagerService.mAppOpsService
|
||||
.noteOperation(AppOpsManager.strOpToOp(op), uid, packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPackagesForUid(int uid) {
|
||||
return mActivityManagerService.mContext.getPackageManager()
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.framework.permission.tests;
|
||||
|
||||
import com.android.internal.os.BinderInternal;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.os.Binder;
|
||||
import android.os.IPermissionController;
|
||||
import android.os.RemoteException;
|
||||
@@ -49,10 +50,16 @@ public class ServiceManagerPermissionTests extends TestCase {
|
||||
public void testSetPermissionController() {
|
||||
try {
|
||||
IPermissionController pc = new IPermissionController.Stub() {
|
||||
@Override
|
||||
public boolean checkPermission(java.lang.String permission, int pid, int uid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int noteOp(String op, int uid, String packageName) {
|
||||
return AppOpsManager.MODE_ALLOWED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPackagesForUid(int uid) {
|
||||
return new String[0];
|
||||
|
||||
Reference in New Issue
Block a user