From: Jeff Sharkey Date: Mon, 16 Apr 2018 15:50:22 +0000 (-0600) Subject: Protect usage data with OP_GET_USAGE_STATS. X-Git-Tag: android-x86-9.0-r1~137^2~4^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=6b649257377b4ba2dd8a2a02b8dd692a72a2cc1e;p=android-x86%2Fframeworks-base.git Protect usage data with OP_GET_USAGE_STATS. APIs that return package usage data (such as the new StatsManager) must ensure that callers hold both the PACKAGE_USAGE_STATS permission and the OP_GET_USAGE_STATS app-op. Add noteOp() method that can be called from native code. Also add missing security checks on command interface. Bug: 77662908, 78121728 Test: builds, boots Change-Id: Ie0d51e4baaacd9d7d36ba0c587ec91a870b9df17 --- diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index f7cc00ced679..31b8df3ee1e9 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -27,8 +27,10 @@ #include "subscriber/SubscriberReporter.h" #include +#include #include #include +#include #include #include #include @@ -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& handlerLooper) : mAnomalyAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, [](const sp& 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& 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) { @@ -660,13 +735,9 @@ status_t StatsService::cmd_clear_puller_cache(FILE* out) { Status StatsService::informAllUidData(const vector& uid, const vector& version, const vector& app) { - VLOG("StatsService::informAllUidData was called"); - - if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { - return Status::fromExceptionCode(Status::EX_SECURITY, - "Only system uid can call informAllUidData"); - } + ENFORCE_UID(AID_SYSTEM); + VLOG("StatsService::informAllUidData was called"); mUidMap->updateMap(getElapsedRealtimeNs(), uid, version, app); VLOG("StatsService::informAllUidData succeeded"); @@ -674,36 +745,26 @@ Status StatsService::informAllUidData(const vector& uid, const vectorgetCallingUid() != 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() { - VLOG("StatsService::informAnomalyAlarmFired was called"); - - if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { - return Status::fromExceptionCode(Status::EX_SECURITY, - "Only system uid can call informAnomalyAlarmFired"); - } + ENFORCE_UID(AID_SYSTEM); + VLOG("StatsService::informAnomalyAlarmFired was called"); int64_t currentTimeSec = getElapsedRealtimeSec(); std::unordered_set, SpHash> alarmSet = mAnomalyAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); @@ -717,14 +778,9 @@ Status StatsService::informAnomalyAlarmFired() { } Status StatsService::informAlarmForSubscriberTriggeringFired() { - VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); - - if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { - return Status::fromExceptionCode( - Status::EX_SECURITY, - "Only system uid can call informAlarmForSubscriberTriggeringFired"); - } + ENFORCE_UID(AID_SYSTEM); + VLOG("StatsService::informAlarmForSubscriberTriggeringFired was called"); int64_t currentTimeSec = getElapsedRealtimeSec(); std::unordered_set, SpHash> alarmSet = mPeriodicAlarmMonitor->popSoonerThan(static_cast(currentTimeSec)); @@ -738,44 +794,28 @@ Status StatsService::informAlarmForSubscriberTriggeringFired() { } Status StatsService::informPollAlarmFired() { - VLOG("StatsService::informPollAlarmFired was called"); - - if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { - return Status::fromExceptionCode(Status::EX_SECURITY, - "Only system uid can call informPollAlarmFired"); - } + ENFORCE_UID(AID_SYSTEM); + VLOG("StatsService::informPollAlarmFired was called"); 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(); } @@ -790,13 +830,9 @@ void StatsService::sayHiToStatsCompanion() { } Status StatsService::statsCompanionReady() { - VLOG("StatsService::statsCompanionReady was called"); - - if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { - return Status::fromExceptionCode(Status::EX_SECURITY, - "Only system uid can call statsCompanionReady"); - } + ENFORCE_UID(AID_SYSTEM); + VLOG("StatsService::statsCompanionReady was called"); sp statsCompanion = getStatsCompanionService(); if (statsCompanion == nullptr) { return Status::fromExceptionCode( @@ -820,33 +856,31 @@ void StatsService::OnLogEvent(LogEvent* event, bool reconnectionStarts) { mProcessor->OnLogEvent(event, reconnectionStarts); } -Status StatsService::getData(int64_t key, vector* output) { +Status StatsService::getData(int64_t key, const String16& packageName, vector* 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* output) { +Status StatsService::getMetadata(const String16& packageName, vector* 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 & config) { - if (!checkCallingPermission(String16(kPermissionDump))) { - return Status::fromExceptionCode(binder::Status::EX_SECURITY); - } +Status StatsService::addConfiguration(int64_t key, const vector & config, + const String16& packageName) { + ENFORCE_DUMP_AND_USAGE_STATS(packageName); + IPCThreadState* ipc = IPCThreadState::self(); if (addConfigurationChecked(ipc->getCallingUid(), key, config)) { return Status::ok(); @@ -869,30 +903,29 @@ bool StatsService::addConfigurationChecked(int uid, int64_t key, const vectorgetCallingUid(), key); mConfigManager->RemoveConfigReceiver(configKey); return Status::ok(); } -Status StatsService::setDataFetchOperation(int64_t key, const sp& intentSender) { - if (!checkCallingPermission(String16(kPermissionDump))) { - return Status::fromExceptionCode(binder::Status::EX_SECURITY); - } +Status StatsService::setDataFetchOperation(int64_t key, + const sp& 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); @@ -902,11 +935,11 @@ Status StatsService::removeConfiguration(int64_t key) { Status StatsService::setBroadcastSubscriber(int64_t configId, int64_t subscriberId, - const sp& intentSender) { + const sp& 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() @@ -915,11 +948,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() @@ -927,7 +960,6 @@ Status StatsService::unsetBroadcastSubscriber(int64_t configId, return Status::ok(); } - void StatsService::binderDied(const wp & who) { ALOGW("statscompanion service died"); mProcessor->WriteDataToDisk(); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index d502796b2167..774a3e94e05b 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -81,36 +81,44 @@ public: /** * Binder call for clients to request data for this configuration key. */ - virtual Status getData(int64_t key, vector* output) override; + virtual Status getData(int64_t key, + const String16& packageName, + vector* output) override; /** * Binder call for clients to get metadata across all configs in statsd. */ - virtual Status getMetadata(vector* output) override; + virtual Status getMetadata(const String16& packageName, + vector* 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& config) override; + virtual Status addConfiguration(int64_t key, + const vector& 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& intentSender) override; + const sp& 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& intentSender) override; + const sp& 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(); diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 5499ee3b7abc..af07f6bee350 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -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 configAsVec(str.begin(), str.end()); bool success; - service.addConfiguration(kConfigKey, configAsVec); + service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str())); } ConfigMetricsReport GetReports(StatsService& service) { vector 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 \ No newline at end of file +} // namespace android diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 8783d945defa..45754aee39c3 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -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(); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 246d4a379eb4..db011dabac9e 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -458,11 +458,11 @@ final class SystemServiceRegistry { }}); registerService(Context.STATS_MANAGER, StatsManager.class, - new StaticServiceFetcher() { - @Override - public StatsManager createService() throws ServiceNotFoundException { - return new StatsManager(); - }}); + new CachedServiceFetcher() { + @Override + public StatsManager createService(ContextImpl ctx) { + return new StatsManager(ctx.getOuterContext()); + }}); registerService(Context.STATUS_BAR_SERVICE, StatusBarManager.class, new CachedServiceFetcher() { diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl index 3de953a2dfbe..dd11d490327b 100644 --- a/core/java/android/os/IPermissionController.aidl +++ b/core/java/android/os/IPermissionController.aidl @@ -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); diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 3b32c52f373b..6b3b93a5bf05 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -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); } diff --git a/data/etc/platform.xml b/data/etc/platform.xml index ed290283a2d2..bde49432a43e 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -175,6 +175,7 @@ + diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2b133593a8d5..a18e7faf21d4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8987,6 +8987,12 @@ public class ActivityManagerService extends IActivityManager.Stub } @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() .getPackagesForUid(uid); diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java index 0504c79c55bd..dcbbdbbcd320 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java @@ -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,11 +50,17 @@ 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]; }