cts: test config changes

Check that app either gets relaunched if it doesn't handle the change,
or receives onConfigurationChanged().

bug: 29367672
Change-Id: I370b82a3315a04939e10a90d04f8292d9738737d
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index 809f8d7..6e7371c 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -46,7 +46,7 @@
         />
         <activity android:name=".NoRelaunchActivity"
                 android:resizeableActivity="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
                 android:exported="true"
                 android:taskAffinity="nobody.but.NoRelaunchActivity"
         />
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
new file mode 100644
index 0000000..e5a121d
--- /dev/null
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.cts;
+
+public class ActivityManagerConfigChangeTests extends ActivityManagerTestBase {
+
+    private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
+
+    public void testRotation90Relaunch() throws Exception{
+        // Should relaunch on every rotation and receive no onConfigurationChanged()
+        testRotation(TEST_ACTIVITY_NAME, 1, 1, 0);
+    }
+
+    public void testRotation90NoRelaunch() throws Exception {
+        // Should receive onConfigurationChanged() on every rotation and no relaunch
+        testRotation(NO_RELAUNCH_ACTIVITY_NAME, 1, 0, 1);
+    }
+
+    public void testRotation180Relaunch() throws Exception {
+        // Should receive nothing
+        testRotation(TEST_ACTIVITY_NAME, 2, 0, 0);
+    }
+
+    public void testRotation180NoRelaunch() throws Exception {
+        // Should receive nothing
+        testRotation(NO_RELAUNCH_ACTIVITY_NAME, 2, 0, 0);
+    }
+
+    public void testChangeFontScaleRelaunch() throws Exception {
+        // Should relaunch and receive no onConfigurationChanged()
+        testChangeFontScale(TEST_ACTIVITY_NAME, true);
+    }
+
+    public void testChangeFontScaleNoRelaunch() throws Exception {
+        // Should receive onConfigurationChanged() and no relaunch
+        testChangeFontScale(NO_RELAUNCH_ACTIVITY_NAME, false);
+    }
+
+    private void testRotation(
+            String activityName, int rotationStep, int numRelaunch, int numConfigChange)
+                    throws Exception {
+        executeShellCommand(getAmStartCmd(activityName));
+
+        final String[] waitForActivitiesVisible = new String[] {activityName};
+        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+        mAmWmState.assertContainsStack(
+                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
+
+        setDeviceRotation(4 - rotationStep);
+        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+
+        for (int rotation = 0; rotation < 4; rotation += rotationStep) {
+            clearLogcat();
+            setDeviceRotation(rotation);
+            mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+            assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange);
+        }
+    }
+
+    private void testChangeFontScale(
+            String activityName, boolean relaunch) throws Exception {
+        executeShellCommand(getAmStartCmd(activityName));
+        final String[] waitForActivitiesVisible = new String[] {activityName};
+        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+        mAmWmState.assertContainsStack(
+                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
+
+        setFontScale(1.0f);
+        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+
+        for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
+            clearLogcat();
+            setFontScale(fontScale);
+            mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+            assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1);
+        }
+    }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index 0100102..6891510 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -99,6 +99,7 @@
 
     private int mInitialAccelerometerRotation;
     private int mUserRotation;
+    private float mFontScale;
 
     @Override
     protected void setUp() throws Exception {
@@ -114,6 +115,7 @@
         // Store rotation settings.
         mInitialAccelerometerRotation = getAccelerometerRotation();
         mUserRotation = getUserRotation();
+        mFontScale = getFontScale();
     }
 
     @Override
@@ -125,6 +127,7 @@
             // Restore rotation settings to the state they were before test.
             setAccelerometerRotation(mInitialAccelerometerRotation);
             setUserRotation(mUserRotation);
+            setFontScale(mFontScale);
             // Remove special stacks.
             executeShellCommand(AM_REMOVE_STACK + PINNED_STACK_ID);
             executeShellCommand(AM_REMOVE_STACK + DOCKED_STACK_ID);
@@ -319,6 +322,22 @@
         }
     }
 
+    protected void setFontScale(float fontScale) throws DeviceNotAvailableException {
+        if (fontScale == 0.0f) {
+            runCommandAndPrintOutput(
+                    "settings delete system font_scale");
+        } else {
+            runCommandAndPrintOutput(
+                    "settings put system font_scale " + fontScale);
+        }
+    }
+
+    protected float getFontScale() throws DeviceNotAvailableException {
+        final String fontScale =
+                runCommandAndPrintOutput("settings get system font_scale").trim();
+        return Float.parseFloat(fontScale);
+    }
+
     protected String runCommandAndPrintOutput(String command) throws DeviceNotAvailableException {
         final String output = executeShellCommand(command);
         log(output);
@@ -359,6 +378,23 @@
         }
     }
 
+    protected void assertRelaunchOrConfigChanged(
+            String activityName, int numRelaunch, int numConfigChange)
+            throws DeviceNotAvailableException {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+
+        if (lifecycleCounts.mDestroyCount != numRelaunch) {
+            fail(activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
+                    + " time(s), expecting " + numRelaunch);
+        } else if (lifecycleCounts.mCreateCount != numRelaunch) {
+            fail(activityName + " has been (re)created " + lifecycleCounts.mCreateCount
+                    + " time(s), expecting " + numRelaunch);
+        } else if (lifecycleCounts.mConfigurationChangedCount != numConfigChange) {
+            fail(activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
+                    + " onConfigurationChanged() calls, expecting " + numConfigChange);
+        }
+    }
+
     private String[] getDeviceLogsForActivity(String activityName)
             throws DeviceNotAvailableException {
         return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", activityName + ":I", "*:S")