Add Default Browser App support and relax Hosts validation for AppLinks

- add private PackageManager APIs for setting/getting the default
Browser App package name
- serialize / deserialize the default Browser App package name per User

Also relax the Hosts name validation for the AppLinls feature. Now we
just care if the IntentFilter is having an HTTP or HTTPS scheme.

Change-Id: I4436f66ac6beff57e14f7f3a2a00b0b582c03be9
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 923b325..83c30aa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -729,37 +729,6 @@
             }
             return false;
         }
-        ArrayList<String> hosts = filter.getHostsList();
-        if (hosts.size() == 0) {
-            if (logging) {
-                Slog.d(TAG, "IntentFilter does not contain any data hosts");
-            }
-            // We still return true as this is the case of any Browser
-            return true;
-        }
-        String hostEndBase = null;
-        for (String host : hosts) {
-            String[] hostParts = host.split("\\.");
-            // Should be at minimum a host like "example.com"
-            if (hostParts.length < 2) {
-                if (logging) {
-                    Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
-                }
-                return false;
-            }
-            // Verify that we have the same ending domain
-            int length = hostParts.length;
-            String hostEnd = hostParts[length - 1] + hostParts[length - 2];
-            if (hostEndBase == null) {
-                hostEndBase = hostEnd;
-            }
-            if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
-                if (logging) {
-                    Slog.d(TAG, "IntentFilter does not contain the same data domains");
-                }
-                return false;
-            }
-        }
         return true;
     }
 
@@ -9100,6 +9069,20 @@
         }
     }
 
+    @Override
+    public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+        synchronized (mPackages) {
+            return mSettings.setDefaultBrowserPackageNameLPr(packageName, userId);
+        }
+    }
+
+    @Override
+    public String getDefaultBrowserPackageName(int userId) {
+        synchronized (mPackages) {
+            return mSettings.getDefaultBrowserPackageNameLPw(userId);
+        }
+    }
+
     /**
      * Get the "allow unknown sources" setting.
      *
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index f294b32..5429517 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageUserState;
 import android.os.storage.VolumeInfo;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.SparseArray;
 
@@ -435,23 +436,23 @@
         userState.delete(userId);
     }
 
-    public IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
+    IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
         return verificationInfo;
     }
 
-    public void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
+    void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
         verificationInfo = info;
     }
 
-    public int getDomainVerificationStatusForUser(int userId) {
+    int getDomainVerificationStatusForUser(int userId) {
         return readUserState(userId).domainVerificationStatus;
     }
 
-    public void setDomainVerificationStatusForUser(int status, int userId) {
+    void setDomainVerificationStatusForUser(int status, int userId) {
         modifyUserState(userId).domainVerificationStatus = status;
     }
 
-    public void clearDomainVerificationStatusForUser(int userId) {
+    void clearDomainVerificationStatusForUser(int userId) {
         modifyUserState(userId).domainVerificationStatus =
                 PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bfcc3db..45bfba1 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -170,6 +170,8 @@
     static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
             "crossProfile-intent-filters";
     public static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
+    public static final String TAG_DEFAULT_APPS= "default-apps";
+    public static final String TAG_DEFAULT_BROWSER= "default-browser";
 
     private static final String ATTR_NAME = "name";
     private static final String ATTR_USER = "user";
@@ -185,6 +187,7 @@
     private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
     private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
+    private static final String ATTR_PACKAGE_NAME= "packageName";
 
     private final Object mLock;
 
@@ -272,7 +275,10 @@
     // names.  The packages appear everwhere else under their original
     // names.
     final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>();
-    
+
+    // For every user, it is used to find the package name of the default Browser App.
+    final SparseArray<String> mDefaultBrowserApp = new SparseArray<String>();
+
     final StringBuilder mReadMessages = new StringBuilder();
 
     /**
@@ -1067,6 +1073,19 @@
         }
     }
 
+    boolean setDefaultBrowserPackageNameLPr(String packageName, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            return false;
+        }
+        mDefaultBrowserApp.put(userId, packageName);
+        writePackageRestrictionsLPr(userId);
+        return true;
+    }
+
+    String getDefaultBrowserPackageNameLPw(int userId) {
+        return (userId == UserHandle.USER_ALL) ? null : mDefaultBrowserApp.get(userId);
+    }
+
     private File getUserPackagesStateFile(int userId) {
         // TODO: Implement a cleaner solution when adding tests.
         // This instead of Environment.getUserSystemDirectory(userId) to support testing.
@@ -1232,15 +1251,25 @@
         Log.d(TAG, "Read domain verification for package:" + ivi.getPackageName());
     }
 
-    void writeDomainVerificationsLPr(XmlSerializer serializer, String packageName,
-                                     IntentFilterVerificationInfo verificationInfo)
-            throws IllegalArgumentException, IllegalStateException, IOException {
-        if (verificationInfo != null && verificationInfo.getPackageName() != null) {
-            serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
-            verificationInfo.writeToXml(serializer);
-            Log.d(TAG, "Wrote domain verification for package: "
-                    + verificationInfo.getPackageName());
-            serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
+    private void readDefaultAppsLPw(XmlPullParser parser, int userId)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_DEFAULT_BROWSER)) {
+                String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+                mDefaultBrowserApp.put(userId, packageName);
+            } else {
+                String msg = "Unknown element under " +  TAG_DEFAULT_APPS + ": " +
+                        parser.getName();
+                PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                XmlUtils.skipCurrentTag(parser);
+            }
         }
     }
 
@@ -1392,6 +1421,8 @@
                     readPersistentPreferredActivitiesLPw(parser, userId);
                 } else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
                     readCrossProfileIntentFiltersLPw(parser, userId);
+                } else if (tagName.equals(TAG_DEFAULT_APPS)) {
+                    readDefaultAppsLPw(parser, userId);
                 } else {
                     Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
                           + parser.getName());
@@ -1490,6 +1521,30 @@
         serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
     }
 
+    void writeDomainVerificationsLPr(XmlSerializer serializer,
+                                     IntentFilterVerificationInfo verificationInfo)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        if (verificationInfo != null && verificationInfo.getPackageName() != null) {
+            serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
+            verificationInfo.writeToXml(serializer);
+            Log.d(TAG, "Wrote domain verification for package: "
+                    + verificationInfo.getPackageName());
+            serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
+        }
+    }
+
+    void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        serializer.startTag(null, TAG_DEFAULT_APPS);
+        String packageName = mDefaultBrowserApp.get(userId);
+        if (!TextUtils.isEmpty(packageName)) {
+            serializer.startTag(null, TAG_DEFAULT_BROWSER);
+            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+            serializer.endTag(null, TAG_DEFAULT_BROWSER);
+        }
+        serializer.endTag(null, TAG_DEFAULT_APPS);
+    }
+
     void writePackageRestrictionsLPr(int userId) {
         if (DEBUG_MU) {
             Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -1600,6 +1655,7 @@
             writePreferredActivitiesLPr(serializer, userId, true);
             writePersistentPreferredActivitiesLPr(serializer, userId);
             writeCrossProfileIntentFiltersLPr(serializer, userId);
+            writeDefaultAppsLPr(serializer, userId);
 
             serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
 
@@ -2114,7 +2170,7 @@
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
         writeKeySetAliasesLPr(serializer, pkg.keySetData);
-        writeDomainVerificationsLPr(serializer, pkg.name, pkg.verificationInfo);
+        writeDomainVerificationsLPr(serializer, pkg.verificationInfo);
 
         serializer.endTag(null, "package");
     }
@@ -2283,8 +2339,9 @@
                     // TODO: check whether this is okay! as it is very
                     // similar to how preferred-activities are treated
                     readCrossProfileIntentFiltersLPw(parser, 0);
-                }
-                else if (tagName.equals("updated-package")) {
+                } else if (tagName.equals(TAG_DEFAULT_BROWSER)) {
+                    readDefaultAppsLPw(parser, 0);
+                } else if (tagName.equals("updated-package")) {
                     readDisabledSysPackageLPw(parser);
                 } else if (tagName.equals("cleaning-package")) {
                     String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -4044,7 +4101,8 @@
 
     void dumpGidsLPr(PrintWriter pw, String prefix, int[] gids) {
         if (!ArrayUtils.isEmpty(gids)) {
-            pw.print(prefix); pw.print("gids="); pw.println(
+            pw.print(prefix);
+            pw.print("gids="); pw.println(
                     PackageManagerService.arrayToString(gids));
         }
     }