Snap for 6492800 from 2037d945ae73b09724c447df48f0fcca2afeeb55 to rvc-release
Change-Id: I6bc60314a53df968cbe61846cb183b4775d54d37
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableFeatureConsistentTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableFeatureConsistentTest.java
deleted file mode 100644
index 48fddc7..0000000
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableFeatureConsistentTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2019 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.appsecurity.cts;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import static android.appsecurity.cts.AdoptableHostTest.FEATURE_ADOPTABLE_STORAGE;
-import android.platform.test.annotations.AppModeFull;
-
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Set of tests that verify behavior of adopted storage media's consistency between the feature
- * flag and what we sniffed from the underlying fstab.
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-@AppModeFull(reason = "Instant applications can only be installed on internal storage")
-public class AdoptableFeatureConsistentTest extends BaseHostJUnit4Test {
-
- private String mHasAdoptable;
-
- @Before
- public void setUp() throws Exception {
- // Caches the initial state of adoptable feature to restore after the tests
- mHasAdoptable = getDevice().executeShellCommand("sm has-adoptable").trim();
- }
-
- @After
- public void tearDown() throws Exception {
- // Restores the initial cache value
- getDevice().executeShellCommand("sm set-force-adoptable" + mHasAdoptable);
- }
-
- @Test
- public void testFeatureTrue() throws Exception {
- getDevice().executeShellCommand("sm set-force-adoptable true");
- checkConsistency();
- }
-
- @Test
- public void testFeatureFalse() throws Exception {
- getDevice().executeShellCommand("sm set-force-adoptable false");
- checkConsistency();
- }
-
- private void checkConsistency() throws Exception {
- // Reboots the device and blocks until the boot complete flag is set.
- getDevice().rebootUntilOnline();
- assertTrue("Device failed to boot", getDevice().waitForBootComplete(120000));
-
- final boolean hasFeature = getDevice().hasFeature(FEATURE_ADOPTABLE_STORAGE);
- final boolean hasFstab = Boolean.parseBoolean(getDevice()
- .executeShellCommand("sm has-adoptable").trim());
- if (hasFeature != hasFstab) {
- fail("Inconsistent adoptable storage status; feature claims " + hasFeature
- + " but fstab claims " + hasFstab);
- }
- }
-}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index 7717623..927f9f5 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -51,7 +51,6 @@
public static final String FEATURE_ADOPTABLE_STORAGE = "feature:android.software.adoptable_storage";
- private boolean mHasAdoptableInitialState;
private String mListVolumesInitialState;
@Before
@@ -59,16 +58,12 @@
// Start all possible users to make sure their storage is unlocked
Utils.prepareMultipleUsers(getDevice(), Integer.MAX_VALUE);
+ // Users are starting, wait for all volumes are ready
+ waitForVolumeReady();
+
// Initial state of all volumes
mListVolumesInitialState = getDevice().executeShellCommand("sm list-volumes");
- // TODO(b/146491109): Revert this change before shipping and find long-term solution.
- // Caches the initial state of adoptable feature and sets it to true (if not already set)
- mHasAdoptableInitialState = Boolean.parseBoolean(
- getDevice().executeShellCommand("sm has-adoptable").trim());
- if (!mHasAdoptableInitialState) {
- setForceAdoptable();
- }
getDevice().uninstallPackage(PKG);
// Enable a virtual disk to give us the best shot at being able to pass
@@ -123,9 +118,19 @@
CLog.w("Volume state is not recovered: " + result);
}
}
- // Restores the initial cache value (if it is different)
- if (!mHasAdoptableInitialState) {
- getDevice().executeShellCommand("sm set-force-adoptable false");
+ }
+
+ /**
+ * Ensure that we have consistency between the feature flag and what we
+ * sniffed from the underlying fstab.
+ */
+ @Test
+ public void testFeatureConsistent() throws Exception {
+ final boolean hasFeature = hasFeature();
+ final boolean hasFstab = hasFstab();
+ if (hasFeature != hasFstab) {
+ fail("Inconsistent adoptable storage status; feature claims " + hasFeature
+ + " but fstab claims " + hasFstab);
}
}
@@ -134,7 +139,7 @@
int attempt = 0;
boolean noCheckingEjecting = false;
String result = "";
- while (!noCheckingEjecting && attempt++ < 20) {
+ while (!noCheckingEjecting && attempt++ < 60) {
result = getDevice().executeShellCommand("sm list-volumes");
noCheckingEjecting = !result.contains("ejecting") && !result.contains("checking");
Thread.sleep(100);
@@ -216,18 +221,6 @@
}
}
- private void setForceAdoptable() throws Exception {
- getDevice().executeShellCommand("sm set-force-adoptable true");
- int attempt = 0;
- boolean hasAdoptable = false;
- while (!hasAdoptable && attempt++ < 5) {
- Thread.sleep(1000);
- hasAdoptable = Boolean.parseBoolean(getDevice()
- .executeShellCommand("sm has-adoptable").trim());
- }
- assertTrue(hasAdoptable);
- }
-
private void verifyPrimaryInternal(String diskId) throws Exception {
// Write some data to shared storage
new InstallMultiple().addFile(APK).run();
@@ -255,6 +248,7 @@
runDeviceTests(PKG, CLASS, "testPrimaryUnmounted");
getDevice().executeShellCommand("sm mount " + vol.volId);
waitForInstrumentationReady();
+ waitForVolumeReady();
runDeviceTests(PKG, CLASS, "testPrimaryAdopted");
runDeviceTests(PKG, CLASS, "testPrimaryDataRead");
@@ -305,6 +299,7 @@
runDeviceTests(PKG, CLASS, "testPrimaryUnmounted");
getDevice().executeShellCommand("sm mount " + vol.volId);
waitForInstrumentationReady();
+ waitForVolumeReady();
runDeviceTests(PKG, CLASS, "testPrimaryAdopted");
runDeviceTests(PKG, CLASS, "testPrimaryDataRead");
@@ -375,6 +370,7 @@
// Kick through a remount cycle, which should purge the adopted app
getDevice().executeShellCommand("sm mount " + vol.volId);
waitForInstrumentationReady();
+ waitForVolumeReady();
runDeviceTests(PKG, CLASS, "testDataInternal");
boolean didThrow = false;
diff --git a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java
index 620fed1..ee3a683 100644
--- a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java
+++ b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java
@@ -188,6 +188,18 @@
mDevice.waitForIdle();
}
+ private String replacePackageAWithPackageB(String path) {
+ return path.replace(mContext.getPackageName(), APPB_PKG);
+ }
+
+ private void testCanNotAccessAppBExternalDirs() {
+ String appBExternalDir = replacePackageAWithPackageB(
+ mContext.getExternalFilesDir("").getParentFile().getAbsolutePath());
+ String appBObbDir = replacePackageAWithPackageB(mContext.getObbDir().getAbsolutePath());
+ assertDirDoesNotExist(appBExternalDir);
+ assertDirDoesNotExist(appBObbDir);
+ }
+
@Test
public void testAppAUnlockDeviceAndVerifyCeDeExternalDataExist() throws Exception {
@@ -221,5 +233,9 @@
testAppAExternalDirsDoExist();
testAppACurProfileDataAccessible();
testAppARefProfileDataNotAccessible();
+
+ // Verify after unlocking device, app a has still no access to app b dir.
+ testCannotAccessAppBDataDir();
+ testCanNotAccessAppBExternalDirs();
}
}
diff --git a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java
index 0f069af..c1e3a53 100644
--- a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java
+++ b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java
@@ -65,7 +65,7 @@
@Test
public void testCanNotAccessAppAExternalDirs() {
String appAExternalDir = replacePackageBWithPackageA(
- mContext.getExternalFilesDir("").getAbsolutePath());
+ mContext.getExternalFilesDir("").getParentFile().getAbsolutePath());
String appAObbDir = replacePackageBWithPackageA(mContext.getObbDir().getAbsolutePath());
assertDirDoesNotExist(appAExternalDir);
assertDirDoesNotExist(appAObbDir);
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 25727f1..8bce302 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -839,9 +839,6 @@
private static final int NETWORK_TIMEOUT_MILLIS = 15000;
private static final String HTTPS_HOST_URL =
"https://connectivitycheck.gstatic.com/generate_204";
- // Minimum and Maximum of iterations of exercise host, @see #doGenerateNetworkTraffic.
- private static final int MIN_EXERCISE_HOST_ITERATIONS = 1;
- private static final int MAX_EXERCISE_HOST_ITERATIONS = 19;
private void doGenerateNetworkTraffic(@NonNull Context context,
@NetworkCapabilities.Transport int transport) throws InterruptedException {
@@ -862,45 +859,9 @@
final long startTime = SystemClock.elapsedRealtime();
try {
- // Since history of network stats only have 2 hours of resolution, when it is
- // being queried, service will assume that history network stats has uniform
- // distribution and return a fraction of network stats that is originally
- // subject to 2 hours. To be specific:
- // <returned network stats> = <total network stats> * <duration> / 2 hour,
- // assuming the duration can fit in a 2 hours bucket.
- // In the other hand, in statsd, the network stats is queried since boot,
- // that means in order to assert non-zero packet counts, either the test should
- // be run after enough time since boot, or the packet counts generated here
- // should be enough. That is to say:
- // <total packet counts> * <up time> / 2 hour >= 1,
- // or
- // iterations >= 2 hour / (<up time> * <packets per iteration>)
- // Thus, iterations can be chosen based on the factors above to make this
- // function generate enough packets in each direction to accommodate enough
- // packet counts for a fraction of history bucket.
- final double iterations = (TimeUnit.HOURS.toMillis(2) / startTime / 7);
- // While just enough iterations are going to make the test flaky, add a 20%
- // buffer to stabilize it and make sure it's in a reasonable range, so it won't
- // consumes more than 100kb of traffic, or generates 0 byte of traffic.
- final int augmentedIterations =
- (int) Math.max(iterations * 1.2, MIN_EXERCISE_HOST_ITERATIONS);
- if (augmentedIterations > MAX_EXERCISE_HOST_ITERATIONS) {
- throw new IllegalStateException("Exceeded max allowed iterations"
- + ", iterations=" + augmentedIterations
- + ", uptime=" + TimeUnit.MILLISECONDS.toSeconds(startTime) + "s");
- }
-
- for (int i = 0; i < augmentedIterations; i++) {
- // By observing results of "dumpsys netstats --uid", typically the single
- // run of the https request below generates 4200/1080 rx/tx bytes with
- // around 7/9 rx/tx packets.
- // This blocks the thread of NetworkCallback, thus no other event
- // can be processed before return.
- exerciseRemoteHost(cm, network, new URL(HTTPS_HOST_URL));
- }
+ exerciseRemoteHost(cm, network, new URL(HTTPS_HOST_URL));
Log.i(TAG, "exerciseRemoteHost successful in " + (SystemClock.elapsedRealtime()
- - startTime) + " ms with iterations=" + augmentedIterations
- + ", uptime=" + TimeUnit.MILLISECONDS.toSeconds(startTime) + "s");
+ - startTime) + " ms");
} catch (Exception e) {
Log.e(TAG, "exerciseRemoteHost failed in " + (SystemClock.elapsedRealtime()
- startTime) + " ms: " + e);
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
index 6d7d95c..3551205 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
@@ -45,12 +45,19 @@
private JobInfo.Builder mBuilder;
private UiDevice mUiDevice;
+ private String mInitialDisplayTimeout;
+
@Override
public void setUp() throws Exception {
super.setUp();
mBuilder = new JobInfo.Builder(STATE_JOB_ID, kJobServiceComponent);
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+
+ // Make sure the screen doesn't turn off when the test turns it on.
+ mInitialDisplayTimeout = mUiDevice.executeShellCommand(
+ "settings get system screen_off_timeout");
+ mUiDevice.executeShellCommand("settings put system screen_off_timeout 300000");
}
@Override
@@ -58,7 +65,12 @@
mJobScheduler.cancel(STATE_JOB_ID);
// Put device back in to normal operation.
toggleScreenOn(true);
- setCarMode(false);
+ if (isCarModeSupported()) {
+ setCarMode(false);
+ }
+
+ mUiDevice.executeShellCommand(
+ "settings put system screen_off_timeout " + mInitialDisplayTimeout);
super.tearDown();
}
@@ -153,6 +165,12 @@
verifyActiveState();
}
+ private boolean isCarModeSupported() {
+ // TVs don't support car mode.
+ return !getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK_ONLY);
+ }
+
/**
* Check if dock state is supported.
*/
@@ -227,6 +245,10 @@
* Ensure car mode is considered active.
*/
public void testCarModePreventsIdle() throws Exception {
+ if (!isCarModeSupported()) {
+ return;
+ }
+
toggleScreenOn(false);
setCarMode(true);
@@ -239,6 +261,10 @@
}
private void runIdleJobStartsOnlyWhenIdle() throws Exception {
+ if (!isCarModeSupported()) {
+ return;
+ }
+
toggleScreenOn(true);
kTestEnvironment.setExpectedExecutions(0);
@@ -276,6 +302,10 @@
}
public void testIdleJobStartsOnlyWhenIdle_carEndsIdle() throws Exception {
+ if (!isCarModeSupported()) {
+ return;
+ }
+
runIdleJobStartsOnlyWhenIdle();
setCarMode(true);
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index a1a5b67..c065b1e 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -73,6 +73,8 @@
private static final long POLL_INTERVAL = 500;
private static final long DEFAULT_WAIT_TIMEOUT = 2000;
private static final long SHELL_TIMEOUT = 3_000;
+ // TODO: mark Settings.System.SCREEN_OFF_TIMEOUT as @TestApi
+ private static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
enum Bucket {
ACTIVE,
@@ -99,6 +101,7 @@
private boolean mInitialWiFiState;
private boolean mInitialAirplaneModeState;
private String mInitialJobSchedulerConstants;
+ private String mInitialDisplayTimeout;
private TestAppInterface mTestAppInterface;
@@ -155,6 +158,10 @@
// Make sure test jobs can run regardless of bucket.
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.JOB_SCHEDULER_CONSTANTS, "min_ready_non_active_jobs_count=0");
+ // Make sure the screen doesn't turn off when the test turns it on.
+ mInitialDisplayTimeout =
+ Settings.System.getString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT);
+ Settings.System.putString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, "300000");
}
@Test
@@ -480,6 +487,9 @@
mUiDevice.executeShellCommand(
"cmd jobscheduler reset-execution-quota -u " + UserHandle.myUserId()
+ " " + TEST_APP_PACKAGE);
+
+ Settings.System.putString(
+ mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, mInitialDisplayTimeout);
}
private void setTestPackageRestricted(boolean restricted) throws Exception {
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 5a8e337..ff2fed4 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -89,6 +89,19 @@
android:screenOrientation="locked"/>
<service
+ android:name=".StubSystemActionsAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService" />
+ <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.accessibilityservice"
+ android:resource="@xml/stub_system_actions_a11y_service" />
+ </service>
+
+ <service
android:name=".StubGestureAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index 73ffb68..7b6be1e 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -147,6 +147,8 @@
<string name="foo_bar_baz">Foo bar baz.</string>
+ <string name="stub_system_actions_a11y_service_description">com.android.accessibilityservice.cts.StubSystemActionsAccessibilityService</string>
+
<string name="stub_gesture_dispatch_a11y_service_description">com.android.accessibilityservice.cts.StubGestureAccessibilityService</string>
<string name="stub_gesture_detector_a11y_service_description">com.android.accessibilityservice.cts.GestureDetectionStubAccessibilityService</string>
diff --git a/tests/accessibilityservice/res/xml/stub_system_actions_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_system_actions_a11y_service.xml
new file mode 100644
index 0000000..e505f5f
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/stub_system_actions_a11y_service.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/stub_system_actions_a11y_service_description"
+/>
\ No newline at end of file
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java
new file mode 100644
index 0000000..f53f126
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 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.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
+import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES;
+import static org.junit.Assert.fail;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
+import android.accessibilityservice.AccessibilityService;
+import android.app.Instrumentation;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.app.UiAutomation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.platform.test.annotations.AppModeFull;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilitySystemActionTest {
+ // intent actions to trigger system action callbacks
+ private final static String INTENT_ACTION_SYSTEM_ACTION_CALLBACK_OVERRIDE_BACK = "android.accessibility.cts.end2endtests.action.system_action_callback_override_back";
+ private final static String INTENT_ACTION_SYSTEM_ACTION_CALLBACK_NEW = "android.accessibility.cts.end2endtests.action.system_action_callback_new";
+
+ private final static int NEW_ACTION_ID = 111;
+ private final static String MANAGE_ACCESSIBILITY_PERMISSION = "android.permission.MANAGE_ACCESSIBILITY";
+
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ private Context mContext;
+ private AccessibilityManager mAccessibilityManager;
+
+ private InstrumentedAccessibilityServiceTestRule<StubSystemActionsAccessibilityService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ StubSystemActionsAccessibilityService.class, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mServiceRule)
+ .around(mDumpOnFailureRule);
+
+ StubSystemActionsAccessibilityService mService;
+
+ @BeforeClass
+ public static void oneTimeSetup() {
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation(FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ sUiAutomation.adoptShellPermissionIdentity(MANAGE_ACCESSIBILITY_PERMISSION);
+ }
+
+ @AfterClass
+ public static void finalTearDown() {
+ sUiAutomation.dropShellPermissionIdentity();
+ sUiAutomation.destroy();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mAccessibilityManager =
+ (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ // Start stub accessibility service.
+ mService = mServiceRule.enableService();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mService.setLatch(null);
+ }
+
+ @Test
+ @AppModeFull
+ public void testRegisterOverriddenLegacyAction() {
+ assertRegisterAction(AccessibilityService.GLOBAL_ACTION_BACK, null);
+ }
+
+ @Test
+ @AppModeFull
+ public void testUnregisterAction() {
+ assertRegisterAction(AccessibilityService.GLOBAL_ACTION_BACK, null);
+ assertUnregisterAction(AccessibilityService.GLOBAL_ACTION_BACK);
+ }
+
+ @Test
+ @AppModeFull
+ public void testNewActionInGetSystemActions() {
+ assertRegisterAction(NEW_ACTION_ID, null);
+ if (!mService.getSystemActions().contains(
+ new AccessibilityAction(NEW_ACTION_ID, null))) {
+ fail("new action should be in getSystemActions() list");
+ }
+ }
+
+
+ @Test
+ @AppModeFull
+ public void testNewActionNotInGetSystemActions() {
+ assertRegisterAction(NEW_ACTION_ID, null);
+ assertUnregisterAction(NEW_ACTION_ID);
+ if (mService.getSystemActions().contains(
+ new AccessibilityAction(NEW_ACTION_ID, null))) {
+ fail("new action should not be in getSystemActions() list");
+ }
+ }
+
+ @Test
+ @AppModeFull
+ public void testPerformOverriddenLegacyAction() {
+ assertRegisterAction(
+ AccessibilityService.GLOBAL_ACTION_BACK,
+ INTENT_ACTION_SYSTEM_ACTION_CALLBACK_OVERRIDE_BACK);
+ assertPerformGlobalAction(
+ AccessibilityService.GLOBAL_ACTION_BACK,
+ INTENT_ACTION_SYSTEM_ACTION_CALLBACK_OVERRIDE_BACK);
+ }
+
+ @Test
+ @AppModeFull
+ public void testPerformNewAction() {
+ assertRegisterAction(NEW_ACTION_ID, INTENT_ACTION_SYSTEM_ACTION_CALLBACK_NEW);
+ assertPerformGlobalAction(NEW_ACTION_ID, INTENT_ACTION_SYSTEM_ACTION_CALLBACK_NEW);
+ }
+
+ private void assertRegisterAction(int actionID, String pendingIntent) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.setLatch(latch);
+ try {
+ RemoteAction r = getRemoteAction(pendingIntent);
+ mAccessibilityManager.registerSystemAction(r, actionID);
+ if (!latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("did not register " + actionID);
+ }
+ } catch (InterruptedException e) {
+ fail("latch.await throws exception");
+ }
+ }
+
+ private void assertUnregisterAction(int actionID) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.setLatch(latch);
+ try {
+ mAccessibilityManager.unregisterSystemAction(actionID);
+ if (!latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("did not unregister " + actionID);
+ }
+ } catch (InterruptedException e) {
+ fail("latch.await throws exception");
+ }
+ }
+
+ private void assertPerformGlobalAction(int actionId, String pendingIntent) {
+ final CountDownLatch receiverLatch = new CountDownLatch(1);
+ BroadcastReceiver receiver = new SystemActionBroadcastReceiver(
+ receiverLatch,
+ pendingIntent);
+ mContext.registerReceiver(receiver, new IntentFilter(pendingIntent));
+ try {
+ mService.performGlobalAction(actionId);
+ if (!receiverLatch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("action not triggered; did not receive callback intent");
+ }
+ } catch (InterruptedException e) {
+ fail("latch.await throws exception");
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ mAccessibilityManager.unregisterSystemAction(actionId);
+ }
+ }
+
+ private RemoteAction getRemoteAction(String pendingIntent) {
+ Intent i = new Intent(pendingIntent);
+ PendingIntent p = PendingIntent.getBroadcast(mContext, 0, i, 0);
+ return new RemoteAction(Icon.createWithContentUri("content://test"), "test1", "test1", p);
+ }
+
+ private static class SystemActionBroadcastReceiver extends BroadcastReceiver {
+ private final CountDownLatch mLatch;
+ private final String mExpectedAction;
+
+ public SystemActionBroadcastReceiver(CountDownLatch latch, String expectedAction) {
+ super();
+ mLatch = latch;
+ mExpectedAction = expectedAction;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (mExpectedAction.equals(action)) {
+ mLatch.countDown();
+ }
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSystemActionsAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSystemActionsAccessibilityService.java
new file mode 100644
index 0000000..b32dc8e
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSystemActionsAccessibilityService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 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.accessibilityservice.cts;
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.content.Intent;
+
+import java.util.concurrent.CountDownLatch;
+
+public class StubSystemActionsAccessibilityService extends InstrumentedAccessibilityService {
+ private CountDownLatch latch;
+
+ @Override
+ public void onSystemActionsChanged() {
+ if (latch != null) {
+ latch.countDown();
+ }
+ }
+
+ void setLatch(CountDownLatch latch) {
+ this.latch = latch;
+ }
+}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index dbb3c2b..fe642c3 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -410,7 +410,7 @@
<activity android:name="android.server.wm.StartActivityAsUserActivity"
android:directBootAware="true"/>
- <activity android:name="android.server.wm.WindowInsetsAnimationTests$TestActivity"
+ <activity android:name="android.server.wm.WindowInsetsAnimationTestBase$TestActivity"
android:theme="@android:style/Theme.Material.NoActionBar" />
<activity android:name="android.server.wm.ForceRelayoutTestBase$TestActivity"
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java
index 9617c7b..5e3e030 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java
@@ -29,6 +29,7 @@
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
+import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.server.wm.WindowManagerState.DisplayContent;
@@ -46,6 +47,7 @@
* atest CtsWindowManagerDeviceTestCases:VrDisplayTests
*/
@Presubmit
+@FlakyTest
@android.server.wm.annotation.Group3
public class VrDisplayTests extends MultiDisplayTestBase {
private static final int VR_VIRTUAL_DISPLAY_WIDTH = 700;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
index 7b38972..6e21f4b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
@@ -44,7 +44,7 @@
import android.graphics.Insets;
import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
-import android.server.wm.WindowInsetsAnimationTests.TestActivity;
+import android.server.wm.WindowInsetsAnimationTestBase.TestActivity;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
new file mode 100644
index 0000000..32255d2
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static android.graphics.Insets.NONE;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.withSettings;
+
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowInsets;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSessionRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+/**
+ * Same as {@link WindowInsetsAnimationTests} but IME specific.
+ *
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationImeTests
+ */
+@Presubmit
+public class WindowInsetsAnimationImeTests extends WindowInsetsAnimationTestBase {
+
+ @Rule
+ public final MockImeSessionRule mMockImeSessionRule = new MockImeSessionRule(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder()
+ );
+
+ @Before
+ public void setup() throws Exception {
+ super.setUp();
+ assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
+ mInstrumentation.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INPUT_METHODS));
+ mActivity = startActivity(TestActivity.class);
+ mRootView = mActivity.getWindow().getDecorView();
+ }
+
+ @Test
+ public void testImeAnimationCallbacksShowAndHide() {
+ WindowInsets before = mActivity.mLastWindowInsets;
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().show(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+ commonAnimationAssertions(mActivity, before, true /* show */, ime());
+ mActivity.mCallback.animationDone = false;
+
+ before = mActivity.mLastWindowInsets;
+
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().hide(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+
+ commonAnimationAssertions(mActivity, before, false /* show */, ime());
+ }
+
+ @Test
+ @FlakyTest(detail = "Promote once confirmed non-flaky")
+ public void testAnimationCallbacks_overlapping_opposite() {
+ WindowInsets before = mActivity.mLastWindowInsets;
+
+ MultiAnimCallback callbackInner = new MultiAnimCallback();
+ MultiAnimCallback callback = mock(MultiAnimCallback.class,
+ withSettings()
+ .spiedInstance(callbackInner)
+ .defaultAnswer(CALLS_REAL_METHODS)
+ .verboseLogging());
+ mActivity.mView.setWindowInsetsAnimationCallback(callback);
+
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().hide(navigationBars()));
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().show(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> callback.animationDone);
+
+ WindowInsets after = mActivity.mLastWindowInsets;
+
+ InOrder inOrder = inOrder(callback, mActivity.mListener);
+
+ inOrder.verify(callback).onPrepare(eq(callback.navBarAnim));
+
+ inOrder.verify(mActivity.mListener).onApplyWindowInsets(any(), argThat(
+ argument -> NONE.equals(argument.getInsets(navigationBars()))
+ && NONE.equals(argument.getInsets(ime()))));
+
+ inOrder.verify(callback).onStart(eq(callback.navBarAnim), argThat(
+ argument -> argument.getLowerBound().equals(NONE)
+ && argument.getUpperBound().equals(before.getInsets(navigationBars()))));
+
+ inOrder.verify(callback).onPrepare(eq(callback.imeAnim));
+ inOrder.verify(mActivity.mListener).onApplyWindowInsets(
+ any(), eq(mActivity.mLastWindowInsets));
+
+ inOrder.verify(callback).onStart(eq(callback.imeAnim), argThat(
+ argument -> argument.getLowerBound().equals(NONE)
+ && !argument.getUpperBound().equals(NONE)));
+
+ inOrder.verify(callback).onEnd(eq(callback.navBarAnim));
+ inOrder.verify(callback).onEnd(eq(callback.imeAnim));
+
+ assertAnimationSteps(callback.navAnimSteps, false /* showAnimation */);
+ assertAnimationSteps(callback.imeAnimSteps, false /* showAnimation */);
+
+ assertEquals(before.getInsets(navigationBars()),
+ callback.navAnimSteps.get(0).insets.getInsets(navigationBars()));
+ assertEquals(after.getInsets(navigationBars()),
+ callback.navAnimSteps.get(callback.navAnimSteps.size() - 1).insets
+ .getInsets(navigationBars()));
+
+ assertEquals(before.getInsets(ime()),
+ callback.imeAnimSteps.get(0).insets.getInsets(ime()));
+ assertEquals(after.getInsets(ime()),
+ callback.imeAnimSteps.get(callback.imeAnimSteps.size() - 1).insets
+ .getInsets(ime()));
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
new file mode 100644
index 0000000..404fd70
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static android.graphics.Insets.NONE;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
+
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.server.wm.WindowInsetsAnimationTestBase.AnimCallback.AnimationStep;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Assert;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Base class for tests in {@link WindowInsetsAnimation} and {@link WindowInsetsAnimation.Callback}.
+ */
+public class WindowInsetsAnimationTestBase extends WindowManagerTestBase {
+
+ protected TestActivity mActivity;
+ protected View mRootView;
+
+ protected void commonAnimationAssertions(TestActivity activity, WindowInsets before,
+ boolean show, int types) {
+
+ AnimCallback callback = activity.mCallback;
+
+ InOrder inOrder = inOrder(activity.mCallback, activity.mListener);
+
+ WindowInsets after = activity.mLastWindowInsets;
+ inOrder.verify(callback).onPrepare(eq(callback.lastAnimation));
+ inOrder.verify(activity.mListener).onApplyWindowInsets(any(), any());
+
+ inOrder.verify(callback).onStart(eq(callback.lastAnimation), argThat(
+ argument -> argument.getLowerBound().equals(NONE)
+ && argument.getUpperBound().equals(show
+ ? after.getInsets(types)
+ : before.getInsets(types))));
+
+ inOrder.verify(callback, atLeast(2)).onProgress(any(), argThat(
+ argument -> argument.size() == 1 && argument.get(0) == callback.lastAnimation));
+ inOrder.verify(callback).onEnd(eq(callback.lastAnimation));
+
+ if ((types & systemBars()) != 0) {
+ assertTrue((callback.lastAnimation.getTypeMask() & systemBars()) != 0);
+ }
+ if ((types & ime()) != 0) {
+ assertTrue((callback.lastAnimation.getTypeMask() & ime()) != 0);
+ }
+ assertTrue(callback.lastAnimation.getDurationMillis() > 0);
+ assertNotNull(callback.lastAnimation.getInterpolator());
+ assertBeforeAfterState(callback.animationSteps, before, after);
+ assertAnimationSteps(callback.animationSteps, show /* increasing */);
+ }
+
+ private void assertBeforeAfterState(ArrayList<AnimationStep> steps, WindowInsets before,
+ WindowInsets after) {
+ assertEquals(before, steps.get(0).insets);
+ assertEquals(after, steps.get(steps.size() - 1).insets);
+ }
+
+ protected void assertAnimationSteps(ArrayList<AnimationStep> steps, boolean showAnimation) {
+ assertTrue(steps.size() >= 2);
+ assertEquals(0f, steps.get(0).fraction, 0f);
+ assertEquals(0f, steps.get(0).interpolatedFraction, 0f);
+ assertEquals(1f, steps.get(steps.size() - 1).fraction, 0f);
+ assertEquals(1f, steps.get(steps.size() - 1).interpolatedFraction, 0f);
+ if (showAnimation) {
+ assertEquals(1f, steps.get(steps.size() - 1).alpha, 0f);
+ } else {
+ assertEquals(1f, steps.get(0).alpha, 0f);
+ }
+
+ assertListElements(steps, step -> step.fraction,
+ (current, next) -> next >= current);
+ assertListElements(steps, step -> step.interpolatedFraction,
+ (current, next) -> next >= current);
+ assertListElements(steps, step -> step.alpha, alpha -> alpha >= 0f);
+ assertListElements(steps, step -> step.insets, compareInsets(systemBars(), showAnimation));
+ }
+
+ private BiPredicate<WindowInsets, WindowInsets> compareInsets(int types,
+ boolean showAnimation) {
+ if (showAnimation) {
+ return (current, next) ->
+ next.getInsets(types).left >= current.getInsets(types).left
+ && next.getInsets(types).top >= current.getInsets(types).top
+ && next.getInsets(types).right >= current.getInsets(types).right
+ && next.getInsets(types).bottom >= current.getInsets(types).bottom;
+ } else {
+ return (current, next) ->
+ next.getInsets(types).left <= current.getInsets(types).left
+ && next.getInsets(types).top <= current.getInsets(types).top
+ && next.getInsets(types).right <= current.getInsets(types).right
+ && next.getInsets(types).bottom <= current.getInsets(types).bottom;
+ }
+ }
+
+ private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
+ Predicate<V> predicate) {
+ for (int i = 0; i <= list.size() - 1; i++) {
+ V value = getter.apply(list.get(i));
+ assertTrue("Predicate.test failed i=" + i + " value="
+ + value, predicate.test(value));
+ }
+ }
+
+ private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
+ BiPredicate<V, V> comparator) {
+ for (int i = 0; i <= list.size() - 2; i++) {
+ V current = getter.apply(list.get(i));
+ V next = getter.apply(list.get(i + 1));
+ assertTrue(comparator.test(current, next));
+ }
+ }
+
+ public static class AnimCallback extends WindowInsetsAnimation.Callback {
+
+ public static class AnimationStep {
+
+ AnimationStep(WindowInsets insets, float fraction, float interpolatedFraction,
+ float alpha) {
+ this.insets = insets;
+ this.fraction = fraction;
+ this.interpolatedFraction = interpolatedFraction;
+ this.alpha = alpha;
+ }
+
+ WindowInsets insets;
+ float fraction;
+ float interpolatedFraction;
+ float alpha;
+ }
+
+ WindowInsetsAnimation lastAnimation;
+ volatile boolean animationDone;
+ final ArrayList<AnimationStep> animationSteps = new ArrayList<>();
+
+ public AnimCallback(int dispatchMode) {
+ super(dispatchMode);
+ }
+
+ @Override
+ public void onPrepare(WindowInsetsAnimation animation) {
+ animationSteps.clear();
+ lastAnimation = animation;
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+ return bounds;
+ }
+
+ @Override
+ public WindowInsets onProgress(WindowInsets insets,
+ List<WindowInsetsAnimation> runningAnimations) {
+ animationSteps.add(new AnimationStep(insets, lastAnimation.getFraction(),
+ lastAnimation.getInterpolatedFraction(), lastAnimation.getAlpha()));
+ return WindowInsets.CONSUMED;
+ }
+
+ @Override
+ public void onEnd(WindowInsetsAnimation animation) {
+ animationDone = true;
+ }
+ }
+
+ protected static class MultiAnimCallback extends WindowInsetsAnimation.Callback {
+
+ WindowInsetsAnimation statusBarAnim;
+ WindowInsetsAnimation navBarAnim;
+ WindowInsetsAnimation imeAnim;
+ volatile boolean animationDone;
+ final ArrayList<AnimationStep> statusAnimSteps = new ArrayList<>();
+ final ArrayList<AnimationStep> navAnimSteps = new ArrayList<>();
+ final ArrayList<AnimationStep> imeAnimSteps = new ArrayList<>();
+ Runnable startRunnable;
+ final ArraySet<WindowInsetsAnimation> runningAnims = new ArraySet<>();
+
+ public MultiAnimCallback() {
+ super(DISPATCH_MODE_STOP);
+ }
+
+ @Override
+ public void onPrepare(WindowInsetsAnimation animation) {
+ if ((animation.getTypeMask() & statusBars()) != 0) {
+ statusBarAnim = animation;
+ }
+ if ((animation.getTypeMask() & navigationBars()) != 0) {
+ navBarAnim = animation;
+ }
+ if ((animation.getTypeMask() & ime()) != 0) {
+ imeAnim = animation;
+ }
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+ if (startRunnable != null) {
+ startRunnable.run();
+ }
+ runningAnims.add(animation);
+ return bounds;
+ }
+
+ @Override
+ public WindowInsets onProgress(WindowInsets insets,
+ List<WindowInsetsAnimation> runningAnimations) {
+ if (statusBarAnim != null) {
+ statusAnimSteps.add(new AnimationStep(insets, statusBarAnim.getFraction(),
+ statusBarAnim.getInterpolatedFraction(), statusBarAnim.getAlpha()));
+ }
+ if (navBarAnim != null) {
+ navAnimSteps.add(new AnimationStep(insets, navBarAnim.getFraction(),
+ navBarAnim.getInterpolatedFraction(), navBarAnim.getAlpha()));
+ }
+ if (imeAnim != null) {
+ imeAnimSteps.add(new AnimationStep(insets, imeAnim.getFraction(),
+ imeAnim.getInterpolatedFraction(), imeAnim.getAlpha()));
+ }
+
+ assertEquals(runningAnims.size(), runningAnimations.size());
+ for (int i = runningAnimations.size() - 1; i >= 0; i--) {
+ Assert.assertNotEquals(-1,
+ runningAnims.indexOf(runningAnimations.get(i)));
+ }
+
+ return WindowInsets.CONSUMED;
+ }
+
+ @Override
+ public void onEnd(WindowInsetsAnimation animation) {
+ runningAnims.remove(animation);
+ if (runningAnims.isEmpty()) {
+ animationDone = true;
+ }
+ }
+ }
+
+ public static class TestActivity extends FocusableActivity {
+
+ AnimCallback mCallback =
+ spy(new AnimCallback(WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP));
+ WindowInsets mLastWindowInsets;
+
+ View.OnApplyWindowInsetsListener mListener;
+ LinearLayout mView;
+ View mChild;
+ EditText mEditor;
+
+ public class InsetsListener implements View.OnApplyWindowInsetsListener {
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mLastWindowInsets = insets;
+ return WindowInsets.CONSUMED;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mListener = spy(new InsetsListener());
+ mView = new LinearLayout(this);
+ mView.setWindowInsetsAnimationCallback(mCallback);
+ mView.setOnApplyWindowInsetsListener(mListener);
+ mChild = new TextView(this);
+ mEditor = new EditText(this);
+ mView.addView(mChild);
+
+ getWindow().setDecorFitsSystemWindows(false);
+ getWindow().getAttributes().layoutInDisplayCutoutMode =
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ setContentView(mView);
+ mEditor.requestFocus();
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
index 86fc5ee..e3a5328 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
@@ -17,54 +17,37 @@
package android.server.wm;
import static android.graphics.Insets.NONE;
-import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.withSettings;
-import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
-import android.server.wm.WindowInsetsAnimationTests.AnimCallback.AnimationStep;
-import android.util.ArraySet;
-import android.view.View;
-import android.view.View.OnApplyWindowInsetsListener;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimation.Callback;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
-import java.util.ArrayList;
import java.util.List;
-import java.util.function.BiPredicate;
-import java.util.function.Function;
-import java.util.function.Predicate;
import androidx.test.filters.FlakyTest;
@@ -75,13 +58,10 @@
* atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationTests
*/
@Presubmit
-public class WindowInsetsAnimationTests extends WindowManagerTestBase {
-
- TestActivity mActivity;
- View mRootView;
+public class WindowInsetsAnimationTests extends WindowInsetsAnimationTestBase {
@Before
- public void setUp() throws Exception {
+ public void setup() throws Exception {
super.setUp();
mActivity = startActivity(TestActivity.class);
mRootView = mActivity.getWindow().getDecorView();
@@ -118,26 +98,6 @@
}
@Test
- public void testImeAnimationCallbacksShowAndHide() {
- WindowInsets before = mActivity.mLastWindowInsets;
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().show(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
- commonAnimationAssertions(mActivity, before, true /* show */, ime());
- mActivity.mCallback.animationDone = false;
-
- before = mActivity.mLastWindowInsets;
-
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().hide(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
-
- commonAnimationAssertions(mActivity, before, false /* show */, ime());
- }
-
- @Test
@FlakyTest(detail = "Promote once confirmed non-flaky")
public void testAnimationCallbacks_overlapping() {
WindowInsets before = mActivity.mLastWindowInsets;
@@ -199,67 +159,6 @@
}
@Test
- @FlakyTest(detail = "Promote once confirmed non-flaky")
- public void testAnimationCallbacks_overlapping_opposite() {
- WindowInsets before = mActivity.mLastWindowInsets;
-
- MultiAnimCallback callbackInner = new MultiAnimCallback();
- MultiAnimCallback callback = mock(MultiAnimCallback.class,
- withSettings()
- .spiedInstance(callbackInner)
- .defaultAnswer(CALLS_REAL_METHODS)
- .verboseLogging());
- mActivity.mView.setWindowInsetsAnimationCallback(callback);
-
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().hide(navigationBars()));
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().show(ime()));
-
- waitForOrFail("Waiting until animation done", () -> callback.animationDone);
-
- WindowInsets after = mActivity.mLastWindowInsets;
-
- InOrder inOrder = inOrder(callback, mActivity.mListener);
-
- inOrder.verify(callback).onPrepare(eq(callback.navBarAnim));
-
- inOrder.verify(mActivity.mListener).onApplyWindowInsets(any(), argThat(
- argument -> NONE.equals(argument.getInsets(navigationBars()))
- && NONE.equals(argument.getInsets(ime()))));
-
- inOrder.verify(callback).onStart(eq(callback.navBarAnim), argThat(
- argument -> argument.getLowerBound().equals(NONE)
- && argument.getUpperBound().equals(before.getInsets(navigationBars()))));
-
- inOrder.verify(callback).onPrepare(eq(callback.imeAnim));
- inOrder.verify(mActivity.mListener).onApplyWindowInsets(
- any(), eq(mActivity.mLastWindowInsets));
-
- inOrder.verify(callback).onStart(eq(callback.imeAnim), argThat(
- argument -> argument.getLowerBound().equals(NONE)
- && !argument.getUpperBound().equals(NONE)));
-
- inOrder.verify(callback).onEnd(eq(callback.navBarAnim));
- inOrder.verify(callback).onEnd(eq(callback.imeAnim));
-
- assertAnimationSteps(callback.navAnimSteps, false /* showAnimation */);
- assertAnimationSteps(callback.imeAnimSteps, false /* showAnimation */);
-
- assertEquals(before.getInsets(navigationBars()),
- callback.navAnimSteps.get(0).insets.getInsets(navigationBars()));
- assertEquals(after.getInsets(navigationBars()),
- callback.navAnimSteps.get(callback.navAnimSteps.size() - 1).insets
- .getInsets(navigationBars()));
-
- assertEquals(before.getInsets(ime()),
- callback.imeAnimSteps.get(0).insets.getInsets(ime()));
- assertEquals(after.getInsets(ime()),
- callback.imeAnimSteps.get(callback.imeAnimSteps.size() - 1).insets
- .getInsets(ime()));
- }
-
- @Test
public void testAnimationCallbacks_consumedByDecor() {
getInstrumentation().runOnMainSync(() -> {
mActivity.getWindow().setDecorFitsSystemWindows(true);
@@ -326,256 +225,5 @@
insets -> NONE.equals(insets.getInsets(navigationBars()))), any());
}
- private void commonAnimationAssertions(TestActivity activity, WindowInsets before,
- boolean show, int types) {
- AnimCallback callback = activity.mCallback;
-
- InOrder inOrder = inOrder(activity.mCallback, activity.mListener);
-
- WindowInsets after = activity.mLastWindowInsets;
- inOrder.verify(callback).onPrepare(eq(callback.lastAnimation));
- inOrder.verify(activity.mListener).onApplyWindowInsets(any(), any());
-
- inOrder.verify(callback).onStart(eq(callback.lastAnimation), argThat(
- argument -> argument.getLowerBound().equals(NONE)
- && argument.getUpperBound().equals(show
- ? after.getInsets(types)
- : before.getInsets(types))));
-
- inOrder.verify(callback, atLeast(2)).onProgress(any(), argThat(
- argument -> argument.size() == 1 && argument.get(0) == callback.lastAnimation));
- inOrder.verify(callback).onEnd(eq(callback.lastAnimation));
-
- if ((types & systemBars()) != 0) {
- assertTrue((callback.lastAnimation.getTypeMask() & systemBars()) != 0);
- }
- if ((types & ime()) != 0) {
- assertTrue((callback.lastAnimation.getTypeMask() & ime()) != 0);
- }
- assertTrue(callback.lastAnimation.getDurationMillis() > 0);
- assertNotNull(callback.lastAnimation.getInterpolator());
- assertBeforeAfterState(callback.animationSteps, before, after);
- assertAnimationSteps(callback.animationSteps, show /* increasing */);
- }
-
- private void assertBeforeAfterState(ArrayList<AnimationStep> steps, WindowInsets before,
- WindowInsets after) {
- assertEquals(before, steps.get(0).insets);
- assertEquals(after, steps.get(steps.size() - 1).insets);
- }
-
- private void assertAnimationSteps(ArrayList<AnimationStep> steps, boolean showAnimation) {
- assertTrue(steps.size() >= 2);
- assertEquals(0f, steps.get(0).fraction, 0f);
- assertEquals(0f, steps.get(0).interpolatedFraction, 0f);
- assertEquals(1f, steps.get(steps.size() - 1).fraction, 0f);
- assertEquals(1f, steps.get(steps.size() - 1).interpolatedFraction, 0f);
- if (showAnimation) {
- assertEquals(1f, steps.get(steps.size() - 1).alpha, 0f);
- } else {
- assertEquals(1f, steps.get(0).alpha, 0f);
- }
-
- assertListElements(steps, step -> step.fraction,
- (current, next) -> next >= current);
- assertListElements(steps, step -> step.interpolatedFraction,
- (current, next) -> next >= current);
- assertListElements(steps, step -> step.alpha, alpha -> alpha >= 0f);
- assertListElements(steps, step -> step.insets, compareInsets(systemBars(), showAnimation));
- }
-
- private BiPredicate<WindowInsets, WindowInsets> compareInsets(int types,
- boolean showAnimation) {
- if (showAnimation) {
- return (current, next) ->
- next.getInsets(types).left >= current.getInsets(types).left
- && next.getInsets(types).top >= current.getInsets(types).top
- && next.getInsets(types).right >= current.getInsets(types).right
- && next.getInsets(types).bottom >= current.getInsets(types).bottom;
- } else {
- return (current, next) ->
- next.getInsets(types).left <= current.getInsets(types).left
- && next.getInsets(types).top <= current.getInsets(types).top
- && next.getInsets(types).right <= current.getInsets(types).right
- && next.getInsets(types).bottom <= current.getInsets(types).bottom;
- }
- }
-
- private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
- Predicate<V> predicate) {
- for (int i = 0; i <= list.size() - 1; i++) {
- V value = getter.apply(list.get(i));
- assertTrue("Predicate.test failed i=" + i + " value=" + value, predicate.test(value));
- }
- }
-
- private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
- BiPredicate<V, V> comparator) {
- for (int i = 0; i <= list.size() - 2; i++) {
- V current = getter.apply(list.get(i));
- V next = getter.apply(list.get(i + 1));
- assertTrue(comparator.test(current, next));
- }
- }
-
- public static class AnimCallback extends WindowInsetsAnimation.Callback {
-
- public static class AnimationStep {
-
- AnimationStep(WindowInsets insets, float fraction, float interpolatedFraction,
- float alpha) {
- this.insets = insets;
- this.fraction = fraction;
- this.interpolatedFraction = interpolatedFraction;
- this.alpha = alpha;
- }
-
- WindowInsets insets;
- float fraction;
- float interpolatedFraction;
- float alpha;
- }
-
- WindowInsetsAnimation lastAnimation;
- volatile boolean animationDone;
- final ArrayList<AnimationStep> animationSteps = new ArrayList<>();
-
- public AnimCallback(int dispatchMode) {
- super(dispatchMode);
- }
-
- @Override
- public void onPrepare(WindowInsetsAnimation animation) {
- animationSteps.clear();
- lastAnimation = animation;
- }
-
- @Override
- public Bounds onStart(WindowInsetsAnimation animation, Bounds bounds) {
- return bounds;
- }
-
- @Override
- public WindowInsets onProgress(WindowInsets insets,
- List<WindowInsetsAnimation> runningAnimations) {
- animationSteps.add(new AnimationStep(insets, lastAnimation.getFraction(),
- lastAnimation.getInterpolatedFraction(), lastAnimation.getAlpha()));
- return WindowInsets.CONSUMED;
- }
-
- @Override
- public void onEnd(WindowInsetsAnimation animation) {
- animationDone = true;
- }
- }
-
- public static class MultiAnimCallback extends WindowInsetsAnimation.Callback {
-
- WindowInsetsAnimation statusBarAnim;
- WindowInsetsAnimation navBarAnim;
- WindowInsetsAnimation imeAnim;
- volatile boolean animationDone;
- final ArrayList<AnimationStep> statusAnimSteps = new ArrayList<>();
- final ArrayList<AnimationStep> navAnimSteps = new ArrayList<>();
- final ArrayList<AnimationStep> imeAnimSteps = new ArrayList<>();
- Runnable startRunnable;
- final ArraySet<WindowInsetsAnimation> runningAnims = new ArraySet<>();
-
- public MultiAnimCallback() {
- super(DISPATCH_MODE_STOP);
- }
-
- @Override
- public void onPrepare(WindowInsetsAnimation animation) {
- if ((animation.getTypeMask() & statusBars()) != 0) {
- statusBarAnim = animation;
- }
- if ((animation.getTypeMask() & navigationBars()) != 0) {
- navBarAnim = animation;
- }
- if ((animation.getTypeMask() & ime()) != 0) {
- imeAnim = animation;
- }
- }
-
- @Override
- public Bounds onStart(WindowInsetsAnimation animation, Bounds bounds) {
- if (startRunnable != null) {
- startRunnable.run();
- }
- runningAnims.add(animation);
- return bounds;
- }
-
- @Override
- public WindowInsets onProgress(WindowInsets insets,
- List<WindowInsetsAnimation> runningAnimations) {
- if (statusBarAnim != null) {
- statusAnimSteps.add(new AnimationStep(insets, statusBarAnim.getFraction(),
- statusBarAnim.getInterpolatedFraction(), statusBarAnim.getAlpha()));
- }
- if (navBarAnim != null) {
- navAnimSteps.add(new AnimationStep(insets, navBarAnim.getFraction(),
- navBarAnim.getInterpolatedFraction(), navBarAnim.getAlpha()));
- }
- if (imeAnim != null) {
- imeAnimSteps.add(new AnimationStep(insets, imeAnim.getFraction(),
- imeAnim.getInterpolatedFraction(), imeAnim.getAlpha()));
- }
-
- assertEquals(runningAnims.size(), runningAnimations.size());
- for (int i = runningAnimations.size() - 1; i >= 0; i--) {
- Assert.assertNotEquals(-1, runningAnims.indexOf(runningAnimations.get(i)));
- }
-
- return WindowInsets.CONSUMED;
- }
-
- @Override
- public void onEnd(WindowInsetsAnimation animation) {
- runningAnims.remove(animation);
- if (runningAnims.isEmpty()) {
- animationDone = true;
- }
- }
- }
-
- public static class TestActivity extends FocusableActivity {
-
- AnimCallback mCallback = spy(new AnimCallback(Callback.DISPATCH_MODE_STOP));
- WindowInsets mLastWindowInsets;
-
- OnApplyWindowInsetsListener mListener;
- LinearLayout mView;
- View mChild;
- EditText mEditor;
-
- public class InsetsListener implements OnApplyWindowInsetsListener {
-
- @Override
- public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
- mLastWindowInsets = insets;
- return WindowInsets.CONSUMED;
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mListener = spy(new InsetsListener());
- mView = new LinearLayout(this);
- mView.setWindowInsetsAnimationCallback(mCallback);
- mView.setOnApplyWindowInsetsListener(mListener);
- mChild = new TextView(this);
- mEditor = new EditText(this);
- mView.addView(mChild);
-
- getWindow().setDecorFitsSystemWindows(false);
- getWindow().getAttributes().layoutInDisplayCutoutMode =
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- setContentView(mView);
- mEditor.requestFocus();
- }
- }
}
diff --git a/tests/tests/appop/AndroidTest.xml b/tests/tests/appop/AndroidTest.xml
index 386feb4..29f01e0 100644
--- a/tests/tests/appop/AndroidTest.xml
+++ b/tests/tests/appop/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAppOpsTestCases.apk" />
<option name="test-file-name" value="CtsAppThatUsesAppOps.apk" />
+ <option name="test-file-name" value="AppInBackground.apk" />
<option name="test-file-name" value="AppThatCanBeForcedIntoForegroundStates.apk" />
</target_preparer>
diff --git a/tests/tests/appop/AppInBackground/Android.bp b/tests/tests/appop/AppInBackground/Android.bp
new file mode 100644
index 0000000..b46e877
--- /dev/null
+++ b/tests/tests/appop/AppInBackground/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 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.
+
+android_test_helper_app {
+ name: "AppInBackground",
+
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ]
+}
diff --git a/tests/tests/appop/AppInBackground/AndroidManifest.xml b/tests/tests/appop/AppInBackground/AndroidManifest.xml
new file mode 100644
index 0000000..00d9ab8
--- /dev/null
+++ b/tests/tests/appop/AppInBackground/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.appops.cts.appinbackground">
+ <attribution android:tag="testAttribution" android:label="@string/dummyLabel" />
+
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+
+ <application />
+</manifest>
diff --git a/tests/tests/appop/AppInBackground/res/values/strings.xml b/tests/tests/appop/AppInBackground/res/values/strings.xml
new file mode 100644
index 0000000..c99e65f
--- /dev/null
+++ b/tests/tests/appop/AppInBackground/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<resources>
+ <string name="dummyLabel">A feature</string>
+</resources>
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt
index 103a4b2..bede62a 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt
@@ -39,6 +39,8 @@
import org.junit.Test
import java.lang.Thread.sleep
+private const val BACKGROUND_PACKAGE = "android.app.appops.cts.appinbackground"
+
class AppOpEventCollectionTest {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
private val context = instrumentation.targetContext
@@ -105,30 +107,86 @@
val attributionOpEntry = opEntry.attributedOpEntries[TEST_ATTRIBUTION_TAG]!!
assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isIn(before..after)
// Access should should also show up in the combined state for all op-flags
assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isIn(before..after)
- assertThat(opEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isIn(before..after)
// Foreground access should should also show up in the combined state for fg and bg
assertThat(attributionOpEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
assertThat(opEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(attributionOpEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
// The access was in foreground, hence there is no background access
- assertThat(attributionOpEntry.getLastBackgroundDuration(OP_FLAG_SELF)).isLessThan(before)
- assertThat(opEntry.getLastBackgroundDuration(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isLessThan(before)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isLessThan(before)
// The access was for a attribution, hence there is no access for the default attribution
if (null in opEntry.attributedOpEntries) {
assertThat(opEntry.attributedOpEntries[null]!!
- .getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
+ .getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
}
// The access does not show up for other op-flags
- assertThat(attributionOpEntry.getLastAccessForegroundTime(
- OP_FLAGS_ALL and OP_FLAG_SELF.inv())).isLessThan(before)
- assertThat(opEntry.getLastAccessForegroundTime(
- OP_FLAGS_ALL and OP_FLAG_SELF.inv())).isLessThan(before)
+ assertThat(
+ attributionOpEntry.getLastAccessForegroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv())
+ ).isLessThan(before)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv()))
+ .isLessThan(before)
+ }
+
+ @Test
+ fun noteInBackgroundWithAttributionAndCheckOpEntries() {
+ val uid = context.packageManager.getPackageUid(BACKGROUND_PACKAGE, 0)
+
+ val before = System.currentTimeMillis()
+ assertThat(
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOp(
+ OPSTR_WIFI_SCAN, uid, BACKGROUND_PACKAGE, TEST_ATTRIBUTION_TAG, null
+ )
+ }
+ ).isEqualTo(AppOpsManager.MODE_ALLOWED)
+ val after = System.currentTimeMillis()
+
+ val opEntry = getOpEntry(uid, BACKGROUND_PACKAGE, OPSTR_WIFI_SCAN)!!
+ val attributionOpEntry = opEntry.attributedOpEntries[TEST_ATTRIBUTION_TAG]!!
+
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isIn(before..after)
+
+ // Access should should also show up in the combined state for all op-flags
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isIn(before..after)
+
+ // Background access should should also show up in the combined state for fg and bg
+ assertThat(attributionOpEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(opEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(attributionOpEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+
+ // The access was in background, hence there is no foreground access
+ assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isLessThan(before)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isLessThan(before)
+
+ // The access was for a attribution, hence there is no access for the default attribution
+ if (null in opEntry.attributedOpEntries) {
+ assertThat(opEntry.attributedOpEntries[null]!!
+ .getLastAccessBackgroundTime(OP_FLAG_SELF)).isLessThan(before)
+ }
+
+ // The access does not show up for other op-flags
+ assertThat(
+ attributionOpEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv())
+ ).isLessThan(before)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv()))
+ .isLessThan(before)
}
@Test
diff --git a/tests/tests/display/OWNERS b/tests/tests/display/OWNERS
index 6b9f7be..041736b 100644
--- a/tests/tests/display/OWNERS
+++ b/tests/tests/display/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 46788
-michaelwr@google.com
\ No newline at end of file
+michaelwr@google.com
+santoscordon@google.com
diff --git a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
index f3b716d..7df46ea 100644
--- a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
+++ b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
@@ -51,10 +51,7 @@
@Before
public void setUp() {
- String version = Build.VERSION.CODENAME;
- // Avoid running these tests before R, on which MediaParser was defined.
- // These check is inlined from BuildCompat to avoid bringing in the entire dependency.
- assumeTrue(version.length() == 1 && version.charAt(0) >= 'R' && version.charAt(0) <= 'Z');
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
}
@Test
diff --git a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
index 5dfcffb..f32ee9e 100644
--- a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
@@ -190,11 +190,11 @@
assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternetSpecifier2));
assertFalse(requestCellularInternet.canBeSatisfiedBy(capVpnInternetSpecifier1));
assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularVpnMmsInternet));
-
- testInvariantInCanBeSatisfiedBy();
}
- private void testInvariantInCanBeSatisfiedBy() {
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testInvariantInCanBeSatisfiedBy() {
// Test invariant that result of NetworkRequest.canBeSatisfiedBy() should be the same with
// NetworkCapabilities.satisfiedByNetworkCapabilities().
final LocalNetworkSpecifier specifier1 = new LocalNetworkSpecifier(1234 /* id */);
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
index 37f90e9..ef2a0c1 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -113,15 +113,6 @@
String extensions = mActivity.getExtensionsString();
- final String es30RequiredList[] = {
- "OES_EGL_image_external_essl3"
- };
-
- for (int i = 0; i < es30RequiredList.length; ++i) {
- assertTrue("OpenGL ES version 3.0+ is missing extension " + es30RequiredList[i],
- hasExtension(extensions, es30RequiredList[i]));
- }
-
if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) < 1)
return;
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 39d8f1b..9bfae3c 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -200,6 +200,34 @@
}
}
+ @AppModeFull(reason = "Uses separate apps for testing")
+ fun testAutoRevoke_whitelistingApis() {
+ withDummyApp {
+ val pm = context.packageManager
+ runWithShellPermissionIdentity {
+ assertFalse(pm.isAutoRevokeWhitelisted(APK_PACKAGE_NAME))
+ }
+
+ runWithShellPermissionIdentity {
+ assertTrue(pm.setAutoRevokeWhitelisted(APK_PACKAGE_NAME, true))
+ }
+ eventually {
+ runWithShellPermissionIdentity {
+ assertTrue(pm.isAutoRevokeWhitelisted(APK_PACKAGE_NAME))
+ }
+ }
+
+ runWithShellPermissionIdentity {
+ assertTrue(pm.setAutoRevokeWhitelisted(APK_PACKAGE_NAME, false))
+ }
+ eventually {
+ runWithShellPermissionIdentity {
+ assertFalse(pm.isAutoRevokeWhitelisted(APK_PACKAGE_NAME))
+ }
+ }
+ }
+ }
+
private fun wakeUpScreen() {
runShellCommand("input keyevent KEYCODE_WAKEUP")
runShellCommand("input keyevent 82")
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index abc63a5..dcc3aef 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -3778,11 +3778,6 @@
<permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
- <!-- Allows an application to manage the companion devices.
- @hide -->
- <permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
- android:protectionLevel="signature" />
-
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 66f1cb3..86e674e 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -2928,16 +2928,7 @@
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
- // Tab should not cause focus to leave the multiline textfield.
- CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_TAB);
- mInstrumentation.waitForIdleSync();
- assertTrue(mTextView.isFocused());
-
- // Tab on the singleline TextView should.
- mActivityRule.runOnUiThread(() -> {
- mTextView.setSingleLine(true);
- });
- mInstrumentation.waitForIdleSync();
+ // Tab should
CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_TAB);
mInstrumentation.waitForIdleSync();
assertFalse(mTextView.isFocused());