OSDN Git Service

Protect usage data with OP_GET_USAGE_STATS.
authorJeff Sharkey <jsharkey@android.com>
Mon, 16 Apr 2018 15:50:22 +0000 (09:50 -0600)
committerJeff Sharkey <jsharkey@android.com>
Mon, 16 Apr 2018 18:44:32 +0000 (12:44 -0600)
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: 7766290878121728
Test: builds, boots
Change-Id: Ie0d51e4baaacd9d7d36ba0c587ec91a870b9df17

cmds/statsd/src/StatsService.cpp
cmds/statsd/src/StatsService.h
cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
core/java/android/app/StatsManager.java
core/java/android/app/SystemServiceRegistry.java
core/java/android/os/IPermissionController.aidl
core/java/android/os/IStatsManager.aidl
data/etc/platform.xml
services/core/java/com/android/server/am/ActivityManagerService.java
tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java

index f7cc00c..31b8df3 100644 (file)
 #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>
 
 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) {
@@ -660,13 +735,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) {
-    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<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() {
-    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<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
             mAnomalyAlarmMonitor->popSoonerThan(static_cast<uint32_t>(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<sp<const InternalAlarm>, SpHash<InternalAlarm>> alarmSet =
             mPeriodicAlarmMonitor->popSoonerThan(static_cast<uint32_t>(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<IStatsCompanionService> 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<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();
@@ -869,30 +903,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);
@@ -902,11 +935,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()
@@ -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 <IBinder>& who) {
     ALOGW("statscompanion service died");
     mProcessor->WriteDataToDisk();
index d502796..774a3e9 100644 (file)
@@ -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();
index 5499ee3..af07f6b 100644 (file)
@@ -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
\ No newline at end of file
+}  // namespace android
index 8783d94..45754ae 100644 (file)
  */
 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();
index 246d4a3..db011da 100644 (file)
@@ -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>() {
index 3de953a..dd11d49 100644 (file)
@@ -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);
index 3b32c52..6b3b93a 100644 (file)
@@ -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);
 }
index ed29028..bde4943 100644 (file)
     <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. -->
index 2b13359..a18e7fa 100644 (file)
@@ -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);
index 0504c79..dcbbdbb 100644 (file)
@@ -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];
                 }