Only relaunch activity on significant size configuration changes.

Currently if the configuration width/height/smallest width changes, we
relaunch the activity or invoke onConfigurationChanged callback. When it
comes to size based configuration changes it might not be necessary: if
the size change doesn't pass one of the threshold defined by the
resources, it means there is no need to relaunch the activity.

In this CL the ActivityManager will receive the thresholds from the
application and use them to decide, whether to relaunch the activity.
The application reads the thresholds from the resources, specifically
from resource qualifiers used by the app.

Change-Id: Ie3cf0a172dc1ba0b865cf30c2962e7cfd9ad8436
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8abebe4..854776a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3897,25 +3897,7 @@
         r.configuration = newConfig;
         r.taskConfigOverride = taskConfig;
 
-        // Determine what has changed.  May be nothing, if this is a config
-        // that has come back from the app after going idle.  In that case
-        // we just want to leave the official config object now in the
-        // activity and do nothing else.
-        int taskChanges = oldTaskOverride.diff(taskConfig);
-        if (taskChanges == 0) {
-            // {@link Configuration#diff} doesn't catch changes from unset values.
-            // Check for changes we care about.
-            if (oldTaskOverride.orientation != taskConfig.orientation) {
-                taskChanges |= ActivityInfo.CONFIG_ORIENTATION;
-            }
-            if (oldTaskOverride.screenHeightDp != taskConfig.screenHeightDp
-                    || oldTaskOverride.screenWidthDp != taskConfig.screenWidthDp) {
-                taskChanges |= ActivityInfo.CONFIG_SCREEN_SIZE;
-            }
-            if (oldTaskOverride.smallestScreenWidthDp != taskConfig.smallestScreenWidthDp) {
-                taskChanges |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-            }
-        }
+        int taskChanges = getTaskConfigurationChanges(r, taskConfig, oldTaskOverride);
         final int changes = oldConfig.diff(newConfig) | taskChanges;
         if (changes == 0 && !r.forceNewConfig) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -3995,6 +3977,72 @@
         return true;
     }
 
+    private int getTaskConfigurationChanges(ActivityRecord record, Configuration taskConfig,
+            Configuration oldTaskOverride) {
+        // Determine what has changed.  May be nothing, if this is a config
+        // that has come back from the app after going idle.  In that case
+        // we just want to leave the official config object now in the
+        // activity and do nothing else.
+        int taskChanges = oldTaskOverride.diff(taskConfig);
+        // We don't want to use size changes if they don't cross boundaries that are important to
+        // the app.
+        if ((taskChanges & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+            final boolean crosses = record.crossesHorizontalSizeThreshold(
+                    oldTaskOverride.screenWidthDp, taskConfig.screenWidthDp)
+                    || record.crossesVerticalSizeThreshold(
+                    oldTaskOverride.screenHeightDp, taskConfig.screenHeightDp);
+            if (!crosses) {
+                taskChanges &= ~ActivityInfo.CONFIG_SCREEN_SIZE;
+            }
+        }
+        if ((taskChanges & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
+            final int oldSmallest = oldTaskOverride.smallestScreenWidthDp;
+            final int newSmallest = taskConfig.smallestScreenWidthDp;
+            final boolean crosses = record.crossesHorizontalSizeThreshold(oldSmallest, newSmallest)
+                    || record.crossesVerticalSizeThreshold(oldSmallest, newSmallest);
+            if (!crosses) {
+                taskChanges &= ~ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+            }
+        }
+        return catchConfigChangesFromUnset(taskConfig, oldTaskOverride, taskChanges);
+    }
+
+    private static int catchConfigChangesFromUnset(Configuration taskConfig,
+            Configuration oldTaskOverride, int taskChanges) {
+        if (taskChanges == 0) {
+            // {@link Configuration#diff} doesn't catch changes from unset values.
+            // Check for changes we care about.
+            if (oldTaskOverride.orientation != taskConfig.orientation) {
+                taskChanges |= ActivityInfo.CONFIG_ORIENTATION;
+            }
+            // We want to explicitly track situations where the size configuration goes from
+            // undefined to defined. We don't care about crossing the threshold in that case,
+            // because there is no threshold.
+            final int oldHeight = oldTaskOverride.screenHeightDp;
+            final int newHeight = taskConfig.screenHeightDp;
+            final int undefinedHeight = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+            if ((oldHeight == undefinedHeight && newHeight != undefinedHeight)
+                    || (oldHeight != undefinedHeight && newHeight == undefinedHeight)) {
+                taskChanges |= ActivityInfo.CONFIG_SCREEN_SIZE;
+            }
+            final int oldWidth = oldTaskOverride.screenWidthDp;
+            final int newWidth = taskConfig.screenWidthDp;
+            final int undefinedWidth = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+            if ((oldWidth == undefinedWidth && newWidth != undefinedWidth)
+                    || (oldWidth != undefinedWidth && newWidth == undefinedWidth)) {
+                taskChanges |= ActivityInfo.CONFIG_SCREEN_SIZE;
+            }
+            final int oldSmallest = oldTaskOverride.smallestScreenWidthDp;
+            final int newSmallest = taskConfig.smallestScreenWidthDp;
+            final int undefinedSmallest = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+            if ((oldSmallest == undefinedSmallest && newSmallest != undefinedSmallest)
+                    || (oldSmallest != undefinedSmallest && newSmallest == undefinedSmallest)) {
+                taskChanges |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+            }
+        }
+        return taskChanges;
+    }
+
     private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) {
         List<ResultInfo> results = null;
         List<ReferrerIntent> newIntents = null;