blob: c8fd5249e94830b86757638da9d89585d741eb37 [file] [log] [blame]
/*
* 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;
import android.platform.test.annotations.Presubmit;
import static android.server.cts.ActivityManagerState.STATE_RESUMED;
import static android.server.cts.StateLogger.logE;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.log.LogUtil.CLog;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Build: mmma -j32 cts/hostsidetests/services
* Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerConfigChangeTests
*/
public class ActivityManagerConfigChangeTests extends ActivityManagerTestBase {
private static final String TEST_ACTIVITY_NAME = "TestActivity";
private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
private static final String FONT_SCALE_ACTIVITY_NAME = "FontScaleActivity";
private static final String FONT_SCALE_NO_RELAUNCH_ACTIVITY_NAME =
"FontScaleNoRelaunchActivity";
private static final float EXPECTED_FONT_SIZE_SP = 10.0f;
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 {
// Will receive a configuration change for rotation.
// TODO(b/36812336): reset configuration change count to 0 once configuration moved out.
testRotation(TEST_ACTIVITY_NAME, 2, 0, 1);
}
public void testRotation180NoRelaunch() throws Exception {
// Should receive one configuration change due to the rotation.
// TODO(b/36812336): reset configuration change count to 0 once configuration moved out.
testRotation(NO_RELAUNCH_ACTIVITY_NAME, 2, 0, 1);
}
@Presubmit
public void testChangeFontScaleRelaunch() throws Exception {
// Should relaunch and receive no onConfigurationChanged()
testChangeFontScale(FONT_SCALE_ACTIVITY_NAME, true /* relaunch */);
}
@Presubmit
public void testChangeFontScaleNoRelaunch() throws Exception {
// Should receive onConfigurationChanged() and no relaunch
testChangeFontScale(FONT_SCALE_NO_RELAUNCH_ACTIVITY_NAME, false /* relaunch */);
}
private void testRotation(
String activityName, int rotationStep, int numRelaunch, int numConfigChange)
throws Exception {
launchActivity(activityName);
final String[] waitForActivitiesVisible = new String[] {activityName};
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
final int initialRotation = 4 - rotationStep;
setDeviceRotation(initialRotation);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
activityName).mStackId;
final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
final int newDeviceRotation = getDeviceRotation(displayId);
if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
CLog.logAndDisplay(LogLevel.WARN, "Got an invalid device rotation value. "
+ "Continuing the test despite of that, but it is likely to fail.");
} else if (newDeviceRotation != initialRotation) {
CLog.logAndDisplay(LogLevel.INFO, "This device doesn't support user rotation "
+ "mode. Not continuing the rotation checks.");
return;
}
for (int rotation = 0; rotation < 4; rotation += rotationStep) {
final String logSeparator = clearLogcat();
setDeviceRotation(rotation);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange, logSeparator);
}
}
private void testChangeFontScale(
String activityName, boolean relaunch) throws Exception {
launchActivity(activityName);
final String[] waitForActivitiesVisible = new String[] {activityName};
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
setFontScale(1.0f);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
final int densityDpi = getGlobalDensityDpi();
for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
final String logSeparator = clearLogcat();
setFontScale(fontScale);
mAmWmState.computeState(mDevice, waitForActivitiesVisible);
assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1,
logSeparator);
// Verify that the display metrics are updated, and therefore the text size is also
// updated accordingly.
assertExpectedFontPixelSize(activityName,
scaledPixelsToPixels(EXPECTED_FONT_SIZE_SP, fontScale, densityDpi),
logSeparator);
}
}
/**
* Test updating application info when app is running. An activity with matching package name
* must be recreated and its asset sequence number must be incremented.
*/
public void testUpdateApplicationInfo() throws Exception {
final String firstLogSeparator = clearLogcat();
// Launch an activity that prints applied config.
launchActivity(TEST_ACTIVITY_NAME);
final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, firstLogSeparator);
final String logSeparator = clearLogcat();
// Update package info.
executeShellCommand("am update-appinfo all " + componentName);
mAmWmState.waitForWithAmState(mDevice, (amState) -> {
// Wait for activity to be resumed and asset seq number to be updated.
try {
return readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator) == assetSeq + 1
&& amState.hasActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
} catch (Exception e) {
logE("Error waiting for valid state: " + e.getMessage());
return false;
}
}, "Waiting asset sequence number to be updated and for activity to be resumed.");
// Check if activity is relaunched and asset seq is updated.
assertRelaunchOrConfigChanged(TEST_ACTIVITY_NAME, 1 /* numRelaunch */,
0 /* numConfigChange */, logSeparator);
final int newAssetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator);
assertEquals("Asset sequence number must be incremented.", assetSeq + 1, newAssetSeq);
}
private static final Pattern sConfigurationPattern = Pattern.compile(
"(.+): Configuration: \\{(.*) as.(\\d+)(.*)\\}");
/** Read asset sequence number in last applied configuration from logs. */
private int readAssetSeqNumber(String activityName, String logSeparator) throws Exception {
final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
final Matcher matcher = sConfigurationPattern.matcher(line);
if (matcher.matches()) {
final String assetSeqNumber = matcher.group(3);
try {
return Integer.valueOf(assetSeqNumber);
} catch (NumberFormatException e) {
// Ignore, asset seq number is not printed when not set.
}
}
}
return 0;
}
// Calculate the scaled pixel size just like the device is supposed to.
private static int scaledPixelsToPixels(float sp, float fontScale, int densityDpi) {
final int DEFAULT_DENSITY = 160;
float f = densityDpi * (1.0f / DEFAULT_DENSITY) * fontScale * sp;
return (int) ((f >= 0) ? (f + 0.5f) : (f - 0.5f));
}
private static Pattern sDeviceDensityPattern =
Pattern.compile(".*?-(l|m|tv|h|xh|xxh|xxxh|\\d+)dpi-.*?");
private int getGlobalDensityDpi() throws Exception {
final String result = getDevice().executeShellCommand("am get-config");
final String[] lines = result.split("\n");
if (lines.length < 1) {
throw new IllegalStateException("Invalid config returned from device: " + result);
}
final Matcher matcher = sDeviceDensityPattern.matcher(lines[0]);
if (!matcher.matches()) {
throw new IllegalStateException("Invalid config returned from device: " + lines[0]);
}
switch (matcher.group(1)) {
case "l": return 120;
case "m": return 160;
case "tv": return 213;
case "h": return 240;
case "xh": return 320;
case "xxh": return 480;
case "xxxh": return 640;
}
return Integer.parseInt(matcher.group(1));
}
private static final Pattern sFontSizePattern = Pattern.compile("^(.+): fontPixelSize=(.+)$");
/** Read the font size in the last log line. */
private void assertExpectedFontPixelSize(String activityName, int fontPixelSize,
String logSeparator) throws Exception {
final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
final Matcher matcher = sFontSizePattern.matcher(line);
if (matcher.matches()) {
assertEquals("Expected font pixel size does not match", fontPixelSize,
Integer.parseInt(matcher.group(2)));
return;
}
}
fail("No fontPixelSize reported from activity " + activityName);
}
}