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/api/current.txt b/api/current.txt
index ccdac7e..9bd6367 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30603,6 +30603,7 @@
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
+    method public java.lang.String getDefaultBrowserPackageName(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
@@ -30650,6 +30651,7 @@
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
+    method public boolean setDefaultBrowserPackageName(java.lang.String, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 7dad2bf..ca6b777 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33205,6 +33205,7 @@
     method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getComponentEnabledSetting(android.content.ComponentName);
     method public android.graphics.drawable.Drawable getDefaultActivityIcon();
+    method public java.lang.String getDefaultBrowserPackageName(int);
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
@@ -33254,6 +33255,7 @@
     method public void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
+    method public boolean setDefaultBrowserPackageName(java.lang.String, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public void verifyPendingInstall(int, int);
   }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ffdc81d..9ddfd88 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1359,6 +1359,26 @@
     }
 
     @Override
+    public String getDefaultBrowserPackageName(int userId) {
+        try {
+            return mPM.getDefaultBrowserPackageName(userId);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return null;
+        }
+    }
+
+    @Override
+    public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+        try {
+            return mPM.setDefaultBrowserPackageName(packageName, userId);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return false;
+        }
+    }
+
+    @Override
     public void setInstallerPackageName(String targetPackage,
             String installerPackageName) {
         try {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 55c990f..c2580c0 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -450,6 +450,9 @@
     List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName);
     List<IntentFilter> getAllIntentFilters(String packageName);
 
+    boolean setDefaultBrowserPackageName(String packageName, int userId);
+    String getDefaultBrowserPackageName(int userId);
+
     VerifierDeviceIdentity getVerifierDeviceIdentity();
 
     boolean isFirstBoot();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 303b709..6532563 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3658,6 +3658,32 @@
     public abstract List<IntentFilter> getAllIntentFilters(String packageName);
 
     /**
+     * Get the default Browser package name for a specific user.
+     *
+     * @param userId The user id.
+     *
+     * @return the package name of the default Browser for the specified user. If the user id passed
+     *         is -1 (all users) it will return a null value.
+     *
+     * @hide
+     */
+    public abstract String getDefaultBrowserPackageName(int userId);
+
+    /**
+     * Set the default Browser package name for a specific user.
+     *
+     * @param packageName The package name of the default Browser.
+     * @param userId The user id.
+     *
+     * @return true if the default Browser for the specified user has been set,
+     *         otherwise return false. If the user id passed is -1 (all users) this call will not
+     *         do anything and just return false.
+     *
+     * @hide
+     */
+    public abstract boolean setDefaultBrowserPackageName(String packageName, int userId);
+
+    /**
      * Change the installer associated with a given package.  There are limitations
      * on how the installer package can be changed; in particular:
      * <ul>
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));
         }
     }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 79510d0..e78750c 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -764,6 +764,16 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public String getDefaultBrowserPackageName(int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @hide
      */