Parse manifest rules for integrity component.

Bug:143689885,145465546
Test: unit test
Change-Id: I146d7e50c9b6bbc06ce0edb0e845b4f01ffff7a9
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 4d97683..6c80a88 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -25,6 +25,7 @@
 import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -41,6 +42,7 @@
 import android.content.pm.Signature;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.RemoteException;
@@ -63,6 +65,8 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
 
 /** Implementation of {@link AppIntegrityManagerService}. */
 public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@@ -72,6 +76,9 @@
     private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
     private static final String PACKAGE_INSTALLER = "com.google.android.packageinstaller";
     private static final String BASE_APK_FILE = "base.apk";
+    private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
+    private static final String ALLOWED_INSTALLER_DELIMITER = ",";
+    private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
 
     private static final String ADB_INSTALLER = "adb";
     private static final String UNKNOWN_INSTALLER = "";
@@ -191,11 +198,21 @@
             Slog.i(TAG, "Received integrity verification intent " + intent.toString());
             Slog.i(TAG, "Extras " + intent.getExtras());
 
-            AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
-
             String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+
+            PackageInfo packageInfo = getPackageArchiveInfo(intent.getData());
+            if (packageInfo == null) {
+                Slog.w(TAG, "Cannot parse package " + packageName);
+                // We can't parse the package.
+                mPackageManagerInternal.setIntegrityVerificationResult(
+                        verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+                return;
+            }
+
             String installerPackageName = getInstallerPackageName(intent);
-            String appCert = getAppCertificateFingerprint(intent.getData());
+            String appCert = getCertificateFingerprint(packageInfo);
+
+            AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
 
             builder.setPackageName(getPackageNameNormalized(packageName));
             builder.setAppCertificate(appCert == null ? "" : appCert);
@@ -208,7 +225,9 @@
             AppInstallMetadata appInstallMetadata = builder.build();
 
             Slog.i(TAG, "To be verified: " + appInstallMetadata);
-            IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata);
+            IntegrityCheckResult result =
+                    mEvaluationEngine.evaluate(
+                            appInstallMetadata, getAllowedInstallers(packageInfo));
             Slog.i(
                     TAG,
                     "Integrity check result: "
@@ -224,14 +243,12 @@
             // This exception indicates something is wrong with the input passed by package manager.
             // e.g., someone trying to trick the system. We block installs in this case.
             Slog.e(TAG, "Invalid input to integrity verification", e);
-
             mPackageManagerInternal.setIntegrityVerificationResult(
                     verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
         } catch (Exception e) {
             // Other exceptions indicate an error within the integrity component implementation and
             // we allow them.
             Slog.e(TAG, "Error handling integrity verification", e);
-
             mPackageManagerInternal.setIntegrityVerificationResult(
                     verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
         }
@@ -320,8 +337,7 @@
         }
     }
 
-    private String getAppCertificateFingerprint(Uri dataUri) {
-        PackageInfo packageInfo = getPackageArchiveInfo(dataUri);
+    private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) {
         return getFingerprint(getSignature(packageInfo));
     }
 
@@ -333,14 +349,50 @@
             PackageInfo installerInfo =
                     mContext.getPackageManager()
                             .getPackageInfo(installer, PackageManager.GET_SIGNATURES);
-            return getFingerprint(getSignature(installerInfo));
+            return getCertificateFingerprint(installerInfo);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.i(TAG, "Installer package " + installer + " not found.");
             return "";
         }
     }
 
-    private static Signature getSignature(PackageInfo packageInfo) {
+    /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */
+    private Map<String, String> getAllowedInstallers(@NonNull PackageInfo packageInfo) {
+        Map<String, String> packageCertMap = new HashMap<>();
+        if (packageInfo.applicationInfo != null && packageInfo.applicationInfo.metaData != null) {
+            Bundle metaData = packageInfo.applicationInfo.metaData;
+            String allowedInstallers = metaData.getString(ALLOWED_INSTALLERS_METADATA_NAME);
+            if (allowedInstallers != null) {
+                // parse the metadata for certs.
+                String[] installerCertPairs = allowedInstallers.split(ALLOWED_INSTALLER_DELIMITER);
+                for (String packageCertPair : installerCertPairs) {
+                    String[] packageAndCert =
+                            packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER);
+                    if (packageAndCert.length == 2) {
+                        String packageName = packageAndCert[0];
+                        String cert = packageAndCert[1];
+                        packageCertMap.put(packageName, cert);
+                    }
+                }
+            }
+        }
+
+        Slog.i("DEBUG", "allowed installers map " + packageCertMap);
+        return packageCertMap;
+    }
+
+    private boolean getPreInstalled(String packageName) {
+        try {
+            PackageInfo existingPackageInfo =
+                    mContext.getPackageManager().getPackageInfo(packageName, 0);
+            return existingPackageInfo.applicationInfo != null
+                    && existingPackageInfo.applicationInfo.isSystemApp();
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    private static Signature getSignature(@NonNull PackageInfo packageInfo) {
         if (packageInfo.signatures == null || packageInfo.signatures.length < 1) {
             throw new IllegalArgumentException("Package signature not found in " + packageInfo);
         }
@@ -402,7 +454,9 @@
                 packageInfo =
                         mContext.getPackageManager()
                                 .getPackageArchiveInfo(
-                                        installationPath.getPath(), PackageManager.GET_SIGNATURES);
+                                        installationPath.getPath(),
+                                        PackageManager.GET_SIGNATURES
+                                                | PackageManager.GET_META_DATA);
             }
             return packageInfo;
         } catch (Exception e) {
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index 1933679..0ea6efc 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -25,6 +25,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * The engine used to evaluate rules against app installs.
@@ -60,7 +61,8 @@
      *     against.
      * @return result of the integrity check
      */
-    public IntegrityCheckResult evaluate(AppInstallMetadata appInstallMetadata) {
+    public IntegrityCheckResult evaluate(
+            AppInstallMetadata appInstallMetadata, Map<String, String> allowedInstallers) {
         List<Rule> rules = loadRules(appInstallMetadata);
         return RuleEvaluator.evaluateRules(rules, appInstallMetadata);
     }
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
index e4c4229..6345c98 100644
--- a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
+++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 222dac9..a2376a6 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -79,7 +79,9 @@
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
 @RunWith(AndroidJUnit4.class)
@@ -288,21 +290,30 @@
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
+        when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
 
         ArgumentCaptor<AppInstallMetadata> metadataCaptor =
                 ArgumentCaptor.forClass(AppInstallMetadata.class);
-        verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
+        Map<String, String> allowedInstallers = new HashMap<>();
+        ArgumentCaptor<Map<String, String>> allowedInstallersCaptor =
+                ArgumentCaptor.forClass(allowedInstallers.getClass());
+        verify(mRuleEvaluationEngine)
+                .evaluate(metadataCaptor.capture(), allowedInstallersCaptor.capture());
         AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
+        allowedInstallers = allowedInstallersCaptor.getValue();
         assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
         assertEquals(APP_CERT, appInstallMetadata.getAppCertificate());
         assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
         assertEquals(INSTALLER_CERT, appInstallMetadata.getInstallerCertificate());
         assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
         assertFalse(appInstallMetadata.isPreInstalled());
+        // These are hardcoded in the test apk
+        assertEquals(2, allowedInstallers.size());
+        assertEquals("cert_1", allowedInstallers.get("store_1"));
+        assertEquals("cert_2", allowedInstallers.get("store_2"));
     }
 
     @Test
@@ -312,7 +323,7 @@
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
         Intent intent = makeVerificationIntent();
-        when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
+        when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
 
         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
         runJobInHandler();
@@ -328,7 +339,7 @@
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
         verify(mMockContext)
                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
-        when(mRuleEvaluationEngine.evaluate(any()))
+        when(mRuleEvaluationEngine.evaluate(any(), any()))
                 .thenReturn(
                         IntegrityCheckResult.deny(
                                 new Rule(