Merge "Introduce DEVICESUMMARY access level to NetworkStatsAccess."
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index e8373a1..1a9bf4e 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -52,10 +52,12 @@
  * {@link NetworkStats.Bucket#STATE_ALL} and all Bucket's roaming is going to be
  * {@link NetworkStats.Bucket#ROAMING_ALL}.
  * <p />
- * <b>NOTE:</b> Accessing stats for apps other than the calling app requires the permission
- * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and
- * will not be granted to third-party apps. However, declaring the permission implies intention to
- * use the API and the user of the device can grant permission through the Settings application.
+ * <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
+ * calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
+ * which is a system-level permission and will not be granted to third-party apps. However,
+ * declaring the permission implies intention to use the API and the user of the device can grant
+ * permission through the Settings application.
+ * <p />
  * Profile owner apps are automatically granted permission to query data on the profile they manage
  * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier-
  * privileged apps likewise get access to usage data for all users on the device.
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
index 53ba718..479b065 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java
@@ -66,15 +66,26 @@
          *
          * <p>Granted to:
          * <ul>
-         * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit
-         * so it is not necessarily sufficient to declare this in the manifest.
-         * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission.
          * <li>Profile owners.
          * </ul>
          */
         int USER = 1;
 
         /**
+         * Access level for apps which can access usage summary of device. Device summary includes
+         * usage by apps running in any profiles/users, however this access level does not
+         * allow querying usage of individual apps running in other profiles/users.
+         *
+         * <p>Granted to:
+         * <ul>
+         * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit
+         * so it is not necessarily sufficient to declare this in the manifest.
+         * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission.
+         * </ul>
+         */
+        int DEVICESUMMARY = 2;
+
+        /**
          * Access level for apps which can access usage for any app on the device, including apps
          * running on other users/profiles.
          *
@@ -85,7 +96,7 @@
          * <li>The system UID.
          * </ul>
          */
-        int DEVICE = 2;
+        int DEVICE = 3;
     }
 
     /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
@@ -107,11 +118,15 @@
             return NetworkStatsAccess.Level.DEVICE;
         }
 
+        boolean hasAppOpsPermission = hasAppOpsPermission(context, callingUid, callingPackage);
+        if (hasAppOpsPermission || context.checkCallingOrSelfPermission(
+                READ_NETWORK_USAGE_HISTORY) == PackageManager.PERMISSION_GRANTED) {
+            return NetworkStatsAccess.Level.DEVICESUMMARY;
+        }
+
         boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
                 DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-        if (hasAppOpsPermission(context, callingUid, callingPackage) || isProfileOwner
-                || context.checkCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY) ==
-                PackageManager.PERMISSION_GRANTED) {
+        if (isProfileOwner) {
             // Apps with the AppOps permission, profile owners, and apps with the privileged
             // permission can access data usage for all apps in this user/profile.
             return NetworkStatsAccess.Level.USER;
@@ -131,6 +146,7 @@
             case NetworkStatsAccess.Level.DEVICE:
                 // Device-level access - can access usage for any uid.
                 return true;
+            case NetworkStatsAccess.Level.DEVICESUMMARY:
             case NetworkStatsAccess.Level.USER:
                 // User-level access - can access usage for any app running in the same user, along
                 // with some special uids (system, removed, or tethering).
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index b1d6f89..3aeceef 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -484,15 +484,19 @@
             public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start,
                     long end) {
                 @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
-                if (accessLevel < NetworkStatsAccess.Level.DEVICE) {
+                if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
                     throw new SecurityException("Calling package " + mCallingPackage
-                            + " cannot access device-level network stats");
+                            + " cannot access device summary network stats");
                 }
                 NetworkStats result = new NetworkStats(end - start, 1);
                 final long ident = Binder.clearCallingIdentity();
                 try {
+                    // Using access level higher than the one we checked for above.
+                    // Reason is that we are combining usage data in a way that is not PII
+                    // anymore.
                     result.combineAllValues(
-                            internalGetSummaryForNetwork(template, start, end, accessLevel));
+                            internalGetSummaryForNetwork(template, start, end,
+                                    NetworkStatsAccess.Level.DEVICE));
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }