Consider removed perms in PrivappPermissionsTest

Fixes:
- Permissions are only granted to the current package. Hence checking
  grant state of permissions on factory package does not make sense
- Permissions might get removed in upgrades of a system package. This
  should be allowed.

Cleanup:
- Have stable order for packages in log
- Use guava set-operations
- Improve readability of output
- Make sure log does not swallow log statements

Test: - atest android.permission2.cts.PrivappPermissionsTest#testPrivappPermissionsEnforcement
      - created a violation and ran test again
      - removed a permission in an updated and ran test again
Merged-In: I107aed66cbd8d2b53de6f4e6035524e6f8e9a6c1
Fixes: 116276904

Change-Id: I8b79d4b5a999342e4f3c2a61810c5f6b745245d2
diff --git a/tests/tests/permission2/Android.mk b/tests/tests/permission2/Android.mk
index e44cd14..c8b71c5 100755
--- a/tests/tests/permission2/Android.mk
+++ b/tests/tests/permission2/Android.mk
@@ -28,7 +28,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	compatibility-device-util \
-	ctstestrunner
+	ctstestrunner \
+	guava
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
index 585f9c6..517f451 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
@@ -16,27 +16,40 @@
 
 package android.permission2.cts;
 
-import com.android.compatibility.common.util.SystemUtil;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+import static com.google.common.collect.Maps.filterValues;
+import static com.google.common.collect.Sets.difference;
+import static com.google.common.collect.Sets.intersection;
+import static com.google.common.collect.Sets.newHashSet;
 
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionInfo;
 import android.support.test.InstrumentationRegistry;
 import android.test.AndroidTestCase;
+import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.compatibility.common.util.PropertyUtil;
+import com.android.compatibility.common.util.SystemUtil;
+
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
-import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
-
 /**
  * Tests enforcement of signature|privileged permission whitelist:
  * <ul>
@@ -65,8 +78,11 @@
         }
 
         List<PackageInfo> installedPackages = pm
-                .getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES);
+                .getInstalledPackages(MATCH_UNINSTALLED_PACKAGES | GET_PERMISSIONS);
+        installedPackages.sort(Comparator.comparing(p -> p.packageName));
 
+        Map<String, Set<String>> packagesGrantedNotInWhitelist = new HashMap<>();
+        Map<String, Set<String>> packagesNotGrantedNotRemovedNotInDenylist = new HashMap<>();
         for (PackageInfo pkg : installedPackages) {
             String packageName = pkg.packageName;
             if (!pkg.applicationInfo.isPrivilegedApp()
@@ -74,57 +90,116 @@
                 continue;
             }
 
-            Set<String> requestedPrivPermissions = new TreeSet<>();
-            Set<String> grantedPrivPermissions = new TreeSet<>();
-            PackageInfo factoryPackageInfo = pm
+            PackageInfo factoryPkg = pm
                     .getPackageInfo(packageName, MATCH_FACTORY_ONLY | GET_PERMISSIONS);
 
-            assertNotNull("No system image version found for " + packageName, factoryPackageInfo);
-            String[] requestedPermissions = factoryPackageInfo.requestedPermissions;
-            int[] requestedPermissionsFlags = factoryPackageInfo.requestedPermissionsFlags;
+            assertNotNull("No system image version found for " + packageName, factoryPkg);
 
-            if (requestedPermissions == null || requestedPermissions.length == 0) {
-                continue;
+            Set<String> factoryRequestedPrivPermissions;
+            if (factoryPkg.requestedPermissions == null) {
+                factoryRequestedPrivPermissions = Collections.emptySet();
+            } else {
+                factoryRequestedPrivPermissions = intersection(
+                        newHashSet(factoryPkg.requestedPermissions), platformPrivPermissions);
             }
-            // Collect 2 sets: requestedPermissions and grantedPrivPermissions
-            for (int i = 0; i < requestedPermissions.length; i++) {
-                String permission = requestedPermissions[i];
-                if (platformPrivPermissions.contains(permission)) {
-                    requestedPrivPermissions.add(permission);
-                    if ((requestedPermissionsFlags[i]
-                            & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
-                        grantedPrivPermissions.add(permission);
+
+            Map<String, Boolean> requestedPrivPermissions = new ArrayMap<>();
+            if (pkg.requestedPermissions != null) {
+                for (int i = 0; i < pkg.requestedPermissions.length; i++) {
+                    String permission = pkg.requestedPermissions[i];
+                    if (platformPrivPermissions.contains(permission)) {
+                        requestedPrivPermissions.put(permission,
+                                (pkg.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
+                                        != 0);
                     }
                 }
             }
+
             // If an app is requesting any privileged permissions, log the details and verify
             // that granted permissions are whitelisted
-            if (!requestedPrivPermissions.isEmpty()) {
-                Set<String> notGranted = new TreeSet<>(requestedPrivPermissions);
-                notGranted.removeAll(grantedPrivPermissions);
+            if (!factoryRequestedPrivPermissions.isEmpty() && !requestedPrivPermissions.isEmpty()) {
+                Set<String> granted = filterValues(requestedPrivPermissions,
+                        isGranted -> isGranted).keySet();
+
+                Set<String> factoryNotGranted = difference(factoryRequestedPrivPermissions,
+                        granted);
+
+                // priv permissions that the system package requested, but the current package not
+                // anymore
+                Set<String> removed = difference(factoryRequestedPrivPermissions,
+                        requestedPrivPermissions.keySet());
+
                 Set<String> whitelist = getPrivAppPermissions(packageName);
                 Set<String> denylist = getPrivAppDenyPermissions(packageName);
-                Log.i(TAG, "Application " + packageName + "."
-                        + " Requested permissions: " + requestedPrivPermissions + "."
-                        + " Granted permissions: " + grantedPrivPermissions + "."
-                        + " Not granted: " + notGranted + "."
-                        + " Whitelisted: " + whitelist + "."
-                        + " Denylisted: " + denylist);
+                String msg = "Application " + packageName + "\n"
+                        + "  Factory requested permissions:\n"
+                        + getPrintableSet("    ", factoryRequestedPrivPermissions)
+                        + "  Granted:\n"
+                        + getPrintableSet("    ", granted)
+                        + "  Removed:\n"
+                        + getPrintableSet("    ", removed)
+                        + "  Whitelisted:\n"
+                        + getPrintableSet("    ", whitelist)
+                        + "  Denylisted:\n"
+                        + getPrintableSet("    ", denylist)
+                        + "  Factory not granted:\n"
+                        + getPrintableSet("    ", factoryNotGranted);
 
-                Set<String> grantedNotInWhitelist = new TreeSet<>(grantedPrivPermissions);
-                grantedNotInWhitelist.removeAll(whitelist);
-                Set<String> notGrantedNotInDenylist = new TreeSet<>(notGranted);
-                notGrantedNotInDenylist.removeAll(denylist);
+                for (String line : msg.split("\n")) {
+                    Log.i(TAG, line);
 
-                assertTrue("Not whitelisted permissions are granted for package "
-                                + packageName + ": " + grantedNotInWhitelist,
-                        grantedNotInWhitelist.isEmpty());
+                    // Prevent log from truncating output
+                    Thread.sleep(10);
+                }
 
-                assertTrue("Requested permissions not granted for package "
-                                + packageName + ": " + notGrantedNotInDenylist,
-                        notGrantedNotInDenylist.isEmpty());
+                Set<String> grantedNotInWhitelist = difference(granted, whitelist);
+                Set<String> factoryNotGrantedNotRemovedNotInDenylist = difference(difference(
+                        factoryNotGranted, removed), denylist);
+
+                if (!grantedNotInWhitelist.isEmpty()) {
+                    packagesGrantedNotInWhitelist.put(packageName, grantedNotInWhitelist);
+                }
+
+                if (!factoryNotGrantedNotRemovedNotInDenylist.isEmpty()) {
+                    packagesNotGrantedNotRemovedNotInDenylist.put(packageName,
+                            factoryNotGrantedNotRemovedNotInDenylist);
+                }
             }
         }
+        StringBuilder message = new StringBuilder();
+        if (!packagesGrantedNotInWhitelist.isEmpty()) {
+            message.append("Not whitelisted permissions are granted: "
+                    + packagesGrantedNotInWhitelist.toString());
+        }
+        if (!packagesNotGrantedNotRemovedNotInDenylist.isEmpty()) {
+            if (message.length() != 0) {
+                message.append(", ");
+            }
+            message.append("Requested permissions not granted: "
+                    + packagesNotGrantedNotRemovedNotInDenylist.toString());
+        }
+        if (!packagesGrantedNotInWhitelist.isEmpty()
+                || !packagesNotGrantedNotRemovedNotInDenylist.isEmpty()) {
+            fail(message.toString());
+        }
+    }
+
+    private <T> String getPrintableSet(String indendation, Set<T> set) {
+        if (set.isEmpty()) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        for (T e : new TreeSet<>(set)) {
+            if (!TextUtils.isEmpty(e.toString().trim())) {
+                sb.append(indendation);
+                sb.append(e);
+                sb.append("\n");
+            }
+        }
+
+        return sb.toString();
     }
 
     private Set<String> getPrivAppPermissions(String packageName) throws IOException {