Merge "Prioritize most-recently-enabled link-handling app" into mnc-dev
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index 96000dd..4dbac05 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -26,7 +26,9 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
+
 import com.android.internal.util.XmlUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -181,14 +183,28 @@
         return getStatusStringFromValue(mMainStatus);
     }
 
-    public static String getStatusStringFromValue(int val) {
-        switch (val) {
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK       : return "ask";
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS    : return "always";
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER     : return "never";
+    public static String getStatusStringFromValue(long val) {
+        StringBuilder sb = new StringBuilder();
+        switch ((int)(val >> 32)) {
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+                sb.append("always : ");
+                sb.append(Long.toHexString(val & 0x00000000FFFFFFFF));
+                break;
+
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                sb.append("ask");
+                break;
+
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+                sb.append("never");
+                break;
+
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
             default:
-            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED : return "undefined";
+                sb.append("undefined");
+                break;
         }
+        return sb.toString();
     }
 
     @Override
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 92b8055..9b28401 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -38,6 +38,7 @@
     public ArraySet<String> enabledComponents;
 
     public int domainVerificationStatus;
+    public int appLinkGeneration;
 
     public PackageUserState() {
         installed = true;
@@ -60,5 +61,6 @@
                 ? new ArraySet<>(o.enabledComponents) : null;
         blockUninstall = o.blockUninstall;
         domainVerificationStatus = o.domainVerificationStatus;
+        appLinkGeneration = o.appLinkGeneration;
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fdc1ff0..c844753 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -283,7 +283,7 @@
     static final boolean DEBUG_PREFERRED = false;
     static final boolean DEBUG_UPGRADE = false;
     static final boolean DEBUG_DOMAIN_VERIFICATION = false;
-    private static final boolean DEBUG_BACKUP = true;
+    private static final boolean DEBUG_BACKUP = false;
     private static final boolean DEBUG_INSTALL = false;
     private static final boolean DEBUG_REMOVE = false;
     private static final boolean DEBUG_BROADCASTS = false;
@@ -4580,7 +4580,8 @@
             if (ps == null) {
                 continue;
             }
-            int status = getDomainVerificationStatusLPr(ps, parentUserId);
+            long verificationState = getDomainVerificationStatusLPr(ps, parentUserId);
+            int status = (int)(verificationState >> 32);
             if (result == null) {
                 result = new CrossProfileDomainInfo();
                 result.resolveInfo =
@@ -4677,11 +4678,17 @@
                         continue;
                     }
                     // Try to get the status from User settings first
-                    int status = getDomainVerificationStatusLPr(ps, userId);
+                    long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+                    int status = (int)(packedStatus >> 32);
+                    int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
                     if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
                         if (DEBUG_DOMAIN_VERIFICATION) {
-                            Slog.i(TAG, "  + always: " + info.activityInfo.packageName);
+                            Slog.i(TAG, "  + always: " + info.activityInfo.packageName
+                                    + " : linkgen=" + linkGeneration);
                         }
+                        // Use link-enabled generation as preferredOrder, i.e.
+                        // prefer newly-enabled over earlier-enabled.
+                        info.preferredOrder = linkGeneration;
                         alwaysList.add(info);
                     } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
                         if (DEBUG_DOMAIN_VERIFICATION) {
@@ -4697,7 +4704,7 @@
                     }
                 }
             }
-            // First try to add the "always" resolution for the current user if there is any
+            // First try to add the "always" resolution(s) for the current user, if any
             if (alwaysList.size() > 0) {
                 result.addAll(alwaysList);
             // if there is an "always" for the parent user, add it.
@@ -4758,15 +4765,19 @@
         return result;
     }
 
-    private int getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
-        int status = ps.getDomainVerificationStatusForUser(userId);
+    // Returns a packed value as a long:
+    //
+    // high 'int'-sized word: link status: undefined/ask/never/always.
+    // low 'int'-sized word: relative priority among 'always' results.
+    private long getDomainVerificationStatusLPr(PackageSetting ps, int userId) {
+        long result = ps.getDomainVerificationStatusForUser(userId);
         // if none available, get the master status
-        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+        if (result >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
             if (ps.getIntentFilterVerificationInfo() != null) {
-                status = ps.getIntentFilterVerificationInfo().getStatus();
+                result = ((long)ps.getIntentFilterVerificationInfo().getStatus()) << 32;
             }
         }
-        return status;
+        return result;
     }
 
     private ResolveInfo querySkipCurrentProfileIntents(
@@ -12962,7 +12973,7 @@
                         false, //hidden
                         null, null, null,
                         false, // blockUninstall
-                        INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+                        INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
                 if (!isSystemApp(ps)) {
                     if (ps.isAnyInstalled(sUserManager.getUserIds())) {
                         // Other user still have this package installed, so all
@@ -14885,8 +14896,8 @@
                             pw.println();
                             count = 0;
                             for (PackageSetting ps : allPackageSettings) {
-                                final int status = ps.getDomainVerificationStatusForUser(userId);
-                                if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                                final long status = ps.getDomainVerificationStatusForUser(userId);
+                                if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
                                     continue;
                                 }
                                 pw.println(prefix + "Package: " + ps.name);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 6f46f69..4faf75a 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -341,7 +341,8 @@
     void setUserState(int userId, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden,
             String lastDisableAppCaller, ArraySet<String> enabledComponents,
-            ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState) {
+            ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState,
+            int linkGeneration) {
         PackageUserState state = modifyUserState(userId);
         state.enabled = enabled;
         state.installed = installed;
@@ -353,6 +354,7 @@
         state.disabledComponents = disabledComponents;
         state.blockUninstall = blockUninstall;
         state.domainVerificationStatus = domainVerifState;
+        state.appLinkGeneration = linkGeneration;
     }
 
     ArraySet<String> getEnabledComponents(int userId) {
@@ -449,12 +451,23 @@
         verificationInfo = info;
     }
 
-    int getDomainVerificationStatusForUser(int userId) {
-        return readUserState(userId).domainVerificationStatus;
+    // Returns a packed value as a long:
+    //
+    // high 'int'-sized word: link status: undefined/ask/never/always.
+    // low 'int'-sized word: relative priority among 'always' results.
+    long getDomainVerificationStatusForUser(int userId) {
+        PackageUserState state = readUserState(userId);
+        long result = (long) state.appLinkGeneration;
+        result |= ((long) state.domainVerificationStatus) << 32;
+        return result;
     }
 
-    void setDomainVerificationStatusForUser(int status, int userId) {
-        modifyUserState(userId).domainVerificationStatus = status;
+    void setDomainVerificationStatusForUser(final int status, int generation, int userId) {
+        PackageUserState state = modifyUserState(userId);
+        state.domainVerificationStatus = status;
+        if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+            state.appLinkGeneration = generation;
+        }
     }
 
     void clearDomainVerificationStatusForUser(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bdcd714..312b7b3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -87,6 +87,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.Xml;
 
 import java.io.BufferedOutputStream;
@@ -196,6 +197,7 @@
     private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
     private static final String ATTR_PACKAGE_NAME= "packageName";
     private static final String ATTR_FINGERPRINT = "fingerprint";
+    private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
 
     private final Object mLock;
 
@@ -294,6 +296,9 @@
     // For every user, it is used to find the package name of the default Browser App.
     final SparseArray<String> mDefaultBrowserApp = new SparseArray<String>();
 
+    // App-link priority tracking, per-user
+    final SparseIntArray mNextAppLinkGeneration = new SparseIntArray();
+
     final StringBuilder mReadMessages = new StringBuilder();
 
     /**
@@ -624,7 +629,7 @@
                                     false, // hidden
                                     null, null, null,
                                     false, // blockUninstall
-                                    INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+                                    INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
                             writePackageRestrictionsLPr(user.id);
                         }
                     }
@@ -1051,7 +1056,7 @@
             }
             return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
         }
-        int status = ps.getDomainVerificationStatusForUser(userId);
+        int status = (int)(ps.getDomainVerificationStatusForUser(userId) >> 32);
         if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
             if (ps.getIntentFilterVerificationInfo() != null) {
                 status = ps.getIntentFilterVerificationInfo().getStatus();
@@ -1060,7 +1065,7 @@
         return status;
     }
 
-    boolean updateIntentFilterVerificationStatusLPw(String packageName, int status, int userId) {
+    boolean updateIntentFilterVerificationStatusLPw(String packageName, final int status, int userId) {
         // Update the status for the current package
         PackageSetting current = mPackages.get(packageName);
         if (current == null) {
@@ -1070,7 +1075,15 @@
             return false;
         }
 
-        current.setDomainVerificationStatusForUser(status, userId);
+        final int alwaysGeneration;
+        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+            alwaysGeneration = mNextAppLinkGeneration.get(userId) + 1;
+            mNextAppLinkGeneration.put(userId, alwaysGeneration);
+        } else {
+            alwaysGeneration = 0;
+        }
+
+        current.setDomainVerificationStatusForUser(status, alwaysGeneration, userId);
         return true;
     }
 
@@ -1382,7 +1395,7 @@
                                 false,  // hidden
                                 null, null, null,
                                 false, // blockUninstall
-                                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
+                                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
                     }
                     return;
                 }
@@ -1404,6 +1417,8 @@
                 return;
             }
 
+            int maxAppLinkGeneration = 0;
+
             int outerDepth = parser.getDepth();
             PackageSetting ps = null;
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1457,6 +1472,12 @@
                             PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED :
                             Integer.parseInt(verifStateStr);
 
+                    final String linkGenStr = parser.getAttributeValue(null, ATTR_APP_LINK_GENERATION);
+                    final int linkGeneration = linkGenStr == null ? 0 : Integer.parseInt(linkGenStr);
+                    if (linkGeneration > maxAppLinkGeneration) {
+                        maxAppLinkGeneration = linkGeneration;
+                    }
+
                     ArraySet<String> enabledComponents = null;
                     ArraySet<String> disabledComponents = null;
 
@@ -1478,7 +1499,7 @@
 
                     ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
                             enabledCaller, enabledComponents, disabledComponents, blockUninstall,
-                            verifState);
+                            verifState, linkGeneration);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
                 } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1496,6 +1517,8 @@
 
             str.close();
 
+            mNextAppLinkGeneration.put(userId, maxAppLinkGeneration + 1);
+
         } catch (XmlPullParserException e) {
             mReadMessages.append("Error reading: " + e.toString());
             PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -1749,6 +1772,10 @@
                         serializer.attribute(null, ATTR_DOMAIN_VERIFICATON_STATE,
                                 Integer.toString(ustate.domainVerificationStatus));
                     }
+                    if (ustate.appLinkGeneration != 0) {
+                        serializer.attribute(null, ATTR_APP_LINK_GENERATION,
+                                Integer.toString(ustate.appLinkGeneration));
+                    }
                     if (ustate.enabledComponents != null
                             && ustate.enabledComponents.size() > 0) {
                         serializer.startTag(null, TAG_ENABLED_COMPONENTS);