Merge "Remove sungsoo@google.com from tests/bluetooth/OWNERS" into main
diff --git a/hostsidetests/packageinstaller/Android.bp b/hostsidetests/packageinstaller/Android.bp
index d6e420f..136b9bc 100644
--- a/hostsidetests/packageinstaller/Android.bp
+++ b/hostsidetests/packageinstaller/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -25,7 +26,7 @@
"tradefed",
"truth",
],
- data: [
+ device_common_data: [
":CtsRootPackageInstallerTestCases",
":CtsRootRollbackManagerHostTestHelperApp",
],
diff --git a/hostsidetests/rollback/Android.bp b/hostsidetests/rollback/Android.bp
index 3a26433..997c9f9 100644
--- a/hostsidetests/rollback/Android.bp
+++ b/hostsidetests/rollback/Android.bp
@@ -27,7 +27,7 @@
"truth",
],
static_libs: ["cts-install-lib-host"],
- data: [":CtsRootRollbackManagerHostTestHelperApp"],
+ device_common_data: [":CtsRootRollbackManagerHostTestHelperApp"],
test_suites: [
"cts_root",
"general-tests",
diff --git a/hostsidetests/rollback/AndroidTest.xml b/hostsidetests/rollback/AndroidTest.xml
index 35e7cda..89fd419 100644
--- a/hostsidetests/rollback/AndroidTest.xml
+++ b/hostsidetests/rollback/AndroidTest.xml
@@ -24,6 +24,10 @@
<option name="test-file-name" value="CtsRootRollbackManagerHostTestHelperApp.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="auto-update-time" value="OFF"/>
+ <option name="auto-update-timezone" value="OFF"/>
+ </target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.cts_root.rollback.host.RollbackManagerHostTest" />
</test>
diff --git a/hostsidetests/rollback/src/com/android/cts_root/rollback/host/WatchdogEventLogger.java b/hostsidetests/rollback/src/com/android/cts_root/rollback/host/WatchdogEventLogger.java
index 59eb027..6aaaf61 100644
--- a/hostsidetests/rollback/src/com/android/cts_root/rollback/host/WatchdogEventLogger.java
+++ b/hostsidetests/rollback/src/com/android/cts_root/rollback/host/WatchdogEventLogger.java
@@ -19,46 +19,36 @@
import static com.google.common.truth.Truth.assertThat;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Truth;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
public class WatchdogEventLogger {
- private static final String[] ROLLBACK_EVENT_TYPES = {
- "ROLLBACK_INITIATE", "ROLLBACK_BOOT_TRIGGERED", "ROLLBACK_SUCCESS"};
- private static final String[] ROLLBACK_EVENT_ATTRS = {
- "logPackage", "rollbackReason", "failedPackageName"};
- private static final String PROP_PREFIX = "persist.sys.rollbacktest.";
private ITestDevice mDevice;
- private void resetProperties(boolean enabled) throws Exception {
+ private void updateTestSysProp(boolean enabled) throws Exception {
assertThat(mDevice.setProperty(
- PROP_PREFIX + "enabled", String.valueOf(enabled))).isTrue();
- for (String type : ROLLBACK_EVENT_TYPES) {
- String key = PROP_PREFIX + type;
- assertThat(mDevice.setProperty(key, "")).isTrue();
- for (String attr : ROLLBACK_EVENT_ATTRS) {
- assertThat(mDevice.setProperty(key + "." + attr, "")).isTrue();
- }
- }
+ "persist.sys.rollbacktest.enabled", String.valueOf(enabled))).isTrue();
}
public void start(ITestDevice device) throws Exception {
mDevice = device;
- resetProperties(true);
+ updateTestSysProp(true);
}
public void stop() throws Exception {
if (mDevice != null) {
- resetProperties(false);
+ updateTestSysProp(false);
}
}
- private boolean matchProperty(String type, String attr, String expectedVal) throws Exception {
- String key = PROP_PREFIX + type + "." + attr;
- String val = mDevice.getProperty(key);
- return expectedVal == null || expectedVal.equals(val);
+ private boolean verifyEventContainsVal(String watchdogEvent, String expectedVal) {
+ return expectedVal == null || watchdogEvent.contains(expectedVal);
}
/**
@@ -68,11 +58,33 @@
* occurred, and return {@code true} if an event exists which matches all criteria.
*/
public boolean watchdogEventOccurred(String type, String logPackage,
- String rollbackReason, String failedPackageName) throws Exception {
- return mDevice.getBooleanProperty(PROP_PREFIX + type, false)
- && matchProperty(type, "logPackage", logPackage)
- && matchProperty(type, "rollbackReason", rollbackReason)
- && matchProperty(type, "failedPackageName", failedPackageName);
+ String rollbackReason, String failedPackageName) {
+ String watchdogEvent = getEventForRollbackType(type);
+ return (watchdogEvent != null)
+ && verifyEventContainsVal(watchdogEvent, logPackage)
+ && verifyEventContainsVal(watchdogEvent, rollbackReason)
+ && verifyEventContainsVal(watchdogEvent, failedPackageName);
+ }
+
+ /** Returns last matched event for rollbackType **/
+ private String getEventForRollbackType(String rollbackType) {
+ String lastMatchedEvent = null;
+ try {
+ String rollbackDump = mDevice.executeShellCommand("dumpsys rollback");
+ String eventRegex = ".*%s%s(.*)\\n";
+ String eventPrefix = "Watchdog event occurred with type: ";
+
+ final Pattern pattern = Pattern.compile(
+ String.format(eventRegex, eventPrefix, rollbackType));
+ final Matcher matcher = pattern.matcher(rollbackDump);
+ while (matcher.find()) {
+ lastMatchedEvent = matcher.group(1);
+ }
+ CLog.d("Found watchdogEvent: " + lastMatchedEvent + " for type: " + rollbackType);
+ } catch (Exception e) {
+ CLog.e("Unable to find event for type: " + rollbackType, e);
+ }
+ return lastMatchedEvent;
}
static class Subject extends com.google.common.truth.Subject {
@@ -93,7 +105,7 @@
}
void eventOccurred(String type, String logPackage, String rollbackReason,
- String failedPackageName) throws Exception {
+ String failedPackageName) {
check("watchdogEventOccurred(type=%s, logPackage=%s, rollbackReason=%s, "
+ "failedPackageName=%s)", type, logPackage, rollbackReason, failedPackageName)
.that(mActual.watchdogEventOccurred(type, logPackage, rollbackReason,
diff --git a/tests/bionic/Android.bp b/tests/bionic/Android.bp
new file mode 100644
index 0000000..62e8102
--- /dev/null
+++ b/tests/bionic/Android.bp
@@ -0,0 +1,14 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "CtsBionicRootTestCases",
+
+ defaults: ["cts_bionic_defaults"],
+
+ test_suites: [
+ "cts_root",
+ ],
+}
+
diff --git a/tests/bionic/AndroidTest.xml b/tests/bionic/AndroidTest.xml
new file mode 100644
index 0000000..b645b76
--- /dev/null
+++ b/tests/bionic/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<configuration description="Config for CTS Bionic root test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="bionic" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <!-- TODO(b/126376458): Remove this when sharding is supported by libgtest_isolated -->
+ <option name="not-shardable" value="true" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsBionicRootTestCases->/data/local/tests/unrestricted/CtsBionicRootTestCases" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tests/unrestricted" />
+ <option name="module-name" value="CtsBionicRootTestCases" />
+ <option name="native-test-timeout" value="4m30s" />
+ <option name="test-case-timeout" value="4m30s" />
+ <option name="runtime-hint" value="12m10s" />
+ </test>
+</configuration>
diff --git a/tests/bionic/OWNERS b/tests/bionic/OWNERS
new file mode 100644
index 0000000..4ba8ede
--- /dev/null
+++ b/tests/bionic/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 14890
+include platform/bionic:/OWNERS
diff --git a/tests/bionic/README.md b/tests/bionic/README.md
new file mode 100644
index 0000000..807d40a
--- /dev/null
+++ b/tests/bionic/README.md
@@ -0,0 +1,10 @@
+# bionic tests
+
+This is the CTS-root boilerplate for the majority of the bionic tests. The
+actual gtests themselves are in bionic/tests/.
+
+See also cts/tests/tests/bionic_app for tests of libc functions in an
+app or JNI context.
+
+See also cts/tests/tests/dynamic_linker/ for some similar testing of
+the dynamic linker.
diff --git a/tests/bluetooth/Android.bp b/tests/bluetooth/Android.bp
index 6e117f1..ccb5125 100644
--- a/tests/bluetooth/Android.bp
+++ b/tests/bluetooth/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_bluetooth",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -27,8 +28,8 @@
"statsd-helper",
],
libs: [
- "android.test.runner",
- "android.test.base",
+ "android.test.runner.stubs.system",
+ "android.test.base.stubs.system",
"statsdprotonano",
],
srcs: ["src/**/*.java"],
@@ -39,6 +40,7 @@
"cts_root",
"general-tests",
"mts-bluetooth",
+ "mts-bt",
],
min_sdk_version: "UpsideDownCake",
}
diff --git a/tests/bluetooth/AndroidTest.xml b/tests/bluetooth/AndroidTest.xml
index 87be477..97bf9d8 100644
--- a/tests/bluetooth/AndroidTest.xml
+++ b/tests/bluetooth/AndroidTest.xml
@@ -46,7 +46,7 @@
<!-- Only run Cts Tests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.android.btservices" />
- <option name="mainline-module-package-name" value="com.google.android.btservices" />
+ <option name="mainline-module-package-name" value="com.android.bt" />
+ <option name="mainline-module-package-name" value="com.google.android.bt" />
</object>
</configuration>
diff --git a/tests/bugreport/Android.bp b/tests/bugreport/Android.bp
index 3c2c368..7c70fef 100644
--- a/tests/bugreport/Android.bp
+++ b/tests/bugreport/Android.bp
@@ -26,8 +26,9 @@
"truth",
],
libs: [
- "android.test.runner",
- "android.test.base",
+ "android.test.runner.stubs.test",
+ "android.test.base.stubs.test",
+ "device_policy_aconfig_flags_lib",
],
data: [":ctsroot-bugreport-artifacts"],
srcs: ["src/**/*.java"],
diff --git a/tests/bugreport/OWNERS b/tests/bugreport/OWNERS
index fca58a6..0819864 100644
--- a/tests/bugreport/OWNERS
+++ b/tests/bugreport/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 153446
-gavincorkery@google.com
+ronish@google.com
nandana@google.com
diff --git a/tests/bugreport/TEST_MAPPING b/tests/bugreport/TEST_MAPPING
index c37dda4..a4ed6fb 100644
--- a/tests/bugreport/TEST_MAPPING
+++ b/tests/bugreport/TEST_MAPPING
@@ -1,12 +1,7 @@
{
"presubmit": [
{
- "name": "CtsRootBugreportTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.LargeTest"
- }
- ]
+ "name": "CtsRootBugreportTestCases"
}
],
"postsubmit": [
diff --git a/tests/bugreport/src/android/bugreport/cts_root/BugreportManagerTest.java b/tests/bugreport/src/android/bugreport/cts_root/BugreportManagerTest.java
index e85de59..53f351d 100644
--- a/tests/bugreport/src/android/bugreport/cts_root/BugreportManagerTest.java
+++ b/tests/bugreport/src/android/bugreport/cts_root/BugreportManagerTest.java
@@ -16,17 +16,27 @@
package android.bugreport.cts_root;
+import static android.app.admin.flags.Flags.FLAG_ONBOARDING_BUGREPORT_STORAGE_BUG_FIX;
+import static android.app.admin.flags.Flags.FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS;
+
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.BugreportManager;
import android.os.BugreportManager.BugreportCallback;
import android.os.BugreportParams;
import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
@@ -38,8 +48,12 @@
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
-import org.junit.After;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -47,6 +61,8 @@
import java.io.File;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -66,16 +82,41 @@
private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
+ private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(4);
+ private static final int MAX_ALLOWED_BUGREPROTS = 8;
+ private static final String INTENT_BUGREPORT_FINISHED =
+ "com.android.internal.intent.action.BUGREPORT_FINISHED";
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+
@Before
- public void setup() {
+ public void setup() throws Exception {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mBugreportManager = mContext.getSystemService(BugreportManager.class);
+ ensureNoConsentDialogShown();
+
+
+ // Unlock before finding/clicking an object.
+ final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ device.wakeUp();
+ device.executeShellCommand("wm dismiss-keyguard");
+ }
+
+ @BeforeClass
+ public static void classSetup() {
+ runShellCommand("settings put global auto_time 0");
+ runShellCommand("svc power stayon true");
// Kill current bugreport, so that it does not interfere with future bugreports.
runShellCommand("setprop ctl.stop bugreportd");
}
- @After
- public void tearDown() {
+ @AfterClass
+ public static void classTearDown() {
+ // Restore auto time
+ runShellCommand("settings put global auto_time 1");
+ runShellCommand("svc power stayon false");
// Kill current bugreport, so that it does not interfere with future bugreports.
runShellCommand("setprop ctl.stop bugreportd");
}
@@ -83,88 +124,368 @@
@LargeTest
@Test
public void testRetrieveBugreportConsentGranted() throws Exception {
- File bugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
- File startBugreportFile = createTempFile("startbugreport", ".zip");
- CountDownLatch latch = new CountDownLatch(1);
- BugreportCallbackImpl callback = new BugreportCallbackImpl(latch);
- mBugreportManager.startBugreport(parcelFd(startBugreportFile), null,
- new BugreportParams(
- BugreportParams.BUGREPORT_MODE_INTERACTIVE,
- BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT),
- mContext.getMainExecutor(), callback);
- latch.await(4, TimeUnit.MINUTES);
- assertThat(callback.isSuccess()).isTrue();
- // No data should be passed to the FD used to call startBugreport.
- assertThat(startBugreportFile.length()).isEqualTo(0);
- String bugreportFileLocation = callback.getBugreportFile();
- waitForDumpstateServiceToStop();
+ try {
+ ensureNotConsentlessReport();
+ File startBugreportFile = createTempFile("startbugreport", ".zip");
+ CountDownLatch latch = new CountDownLatch(1);
+ BugreportCallbackImpl callback = new BugreportCallbackImpl(latch);
+ mBugreportManager.startBugreport(parcelFd(startBugreportFile), null,
+ new BugreportParams(
+ BugreportParams.BUGREPORT_MODE_ONBOARDING,
+ BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT),
+ mContext.getMainExecutor(), callback);
+ latch.await(4, TimeUnit.MINUTES);
+ assertThat(callback.isSuccess()).isTrue();
+ // No data should be passed to the FD used to call startBugreport.
+ assertThat(startBugreportFile.length()).isEqualTo(0);
+ String bugreportFileLocation = callback.getBugreportFile();
+ waitForDumpstateServiceToStop();
+ // Trying to retrieve an unknown bugreport should fail
+ latch = new CountDownLatch(1);
+ callback = new BugreportCallbackImpl(latch);
+ File bugreportFile2 = createTempFile("bugreport2_" + name.getMethodName(), ".zip");
+ mBugreportManager.retrieveBugreport(
+ "unknown/file.zip", parcelFd(bugreportFile2),
+ mContext.getMainExecutor(), callback);
+ assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
+ assertThat(callback.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
+ waitForDumpstateServiceToStop();
-
- // Trying to retrieve an unknown bugreport should fail
- latch = new CountDownLatch(1);
- callback = new BugreportCallbackImpl(latch);
- File bugreportFile2 = createTempFile("bugreport2_" + name.getMethodName(), ".zip");
- mBugreportManager.retrieveBugreport(
- "unknown/file.zip", parcelFd(bugreportFile2),
- mContext.getMainExecutor(), callback);
- assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
- assertThat(callback.getErrorCode()).isEqualTo(
- BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
-
- // A bugreport was previously generated for this caller. When the consent dialog is invoked
- // and accepted, the bugreport files should be passed to the calling package.
- ParcelFileDescriptor bugreportFd = parcelFd(bugreportFile);
- assertThat(bugreportFd).isNotNull();
- latch = new CountDownLatch(1);
- mBugreportManager.retrieveBugreport(bugreportFileLocation, bugreportFd,
- mContext.getMainExecutor(), new BugreportCallbackImpl(latch));
- shareConsentDialog(ConsentReply.ALLOW);
- assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue();
- assertThat(bugreportFile.length()).isGreaterThan(0);
+ File bugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+ // A bugreport was previously generated for this caller. When the consent dialog is invoked
+ // and accepted, the bugreport files should be passed to the calling package.
+ ParcelFileDescriptor bugreportFd = parcelFd(bugreportFile);
+ assertThat(bugreportFd).isNotNull();
+ latch = new CountDownLatch(1);
+ mBugreportManager.retrieveBugreport(bugreportFileLocation, bugreportFd,
+ mContext.getMainExecutor(), new BugreportCallbackImpl(latch));
+ shareConsentDialog(ConsentReply.ALLOW);
+ assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue();
+ assertThat(bugreportFile.length()).isGreaterThan(0);
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
}
@LargeTest
@Test
public void testRetrieveBugreportConsentDenied() throws Exception {
- File bugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+ try {
+ // User denies consent, therefore no data should be passed back to the bugreport file.
+ ensureNotConsentlessReport();
+ CountDownLatch latch = new CountDownLatch(1);
+ BugreportCallbackImpl callback = new BugreportCallbackImpl(latch);
+ mBugreportManager.startBugreport(parcelFd(new File("/dev/null")),
+ null, new BugreportParams(BugreportParams.BUGREPORT_MODE_ONBOARDING,
+ BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT),
+ mContext.getMainExecutor(), callback);
+ latch.await(4, TimeUnit.MINUTES);
+ assertThat(callback.isSuccess()).isTrue();
+ String bugreportFileLocation = callback.getBugreportFile();
+ waitForDumpstateServiceToStop();
- // User denies consent, therefore no data should be passed back to the bugreport file.
+ latch = new CountDownLatch(1);
+ callback = new BugreportCallbackImpl(latch);
+ File bugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+ ParcelFileDescriptor bugreportFd = parcelFd(bugreportFile);
+ assertThat(bugreportFd).isNotNull();
+ mBugreportManager.retrieveBugreport(
+ bugreportFileLocation,
+ bugreportFd,
+ mContext.getMainExecutor(),
+ callback);
+ shareConsentDialog(ConsentReply.DENY);
+ latch.await(1, TimeUnit.MINUTES);
+ assertThat(callback.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_USER_DENIED_CONSENT);
+ assertThat(bugreportFile.length()).isEqualTo(0);
+ waitForDumpstateServiceToStop();
+
+ // Since consent has already been denied, this call should fail because consent cannot
+ // be requested twice for the same bugreport.
+ latch = new CountDownLatch(1);
+ callback = new BugreportCallbackImpl(latch);
+ mBugreportManager.retrieveBugreport(bugreportFileLocation, parcelFd(bugreportFile),
+ mContext.getMainExecutor(), callback);
+ latch.await(1, TimeUnit.MINUTES);
+ assertThat(callback.getErrorCode()).isEqualTo(
+ BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
+ waitForDumpstateServiceToStop();
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_ONBOARDING_BUGREPORT_STORAGE_BUG_FIX)
+ public void testBugreportsLimitReached() throws Exception {
+ try {
+ List<File> bugreportFiles = new ArrayList<>();
+ List<String> bugreportFileLocations = new ArrayList<>();
+ CountDownLatch latch = new CountDownLatch(1);
+
+ for (int i = 0; i < MAX_ALLOWED_BUGREPROTS + 1; i++) {
+ waitForDumpstateServiceToStop();
+ File bugreportFile = createTempFile(
+ "bugreport_" + name.getMethodName() + "_" + i, ".zip");
+ bugreportFiles.add(bugreportFile);
+ File startBugreportFile = createTempFile("startbugreport", ".zip");
+
+ latch = new CountDownLatch(1);
+ BugreportCallbackImpl callback = new BugreportCallbackImpl(latch);
+
+ mBugreportManager.startBugreport(parcelFd(startBugreportFile), null,
+ new BugreportParams(
+ BugreportParams.BUGREPORT_MODE_ONBOARDING,
+ BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT),
+ mContext.getMainExecutor(), callback);
+
+ latch.await(BUGREPORT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertThat(callback.isSuccess()).isTrue();
+ bugreportFileLocations.add(callback.getBugreportFile());
+ waitForDumpstateServiceToStop();
+ }
+
+ final long newTime = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(10);
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ mContext.getSystemService(AlarmManager.class).setTime(newTime));
+
+ // Trigger a shell bugreport to trigger cleanup logic
+ triggerShellBugreport(BugreportParams.BUGREPORT_MODE_ONBOARDING);
+
+ // The retrieved first bugreport file should be empty.
+ latch = new CountDownLatch(1);
+ BugreportCallbackImpl callback = new BugreportCallbackImpl(latch);
+ mBugreportManager.retrieveBugreport(
+ bugreportFileLocations.getFirst(), parcelFd(bugreportFiles.getFirst()),
+ mContext.getMainExecutor(), callback);
+ ensureNotConsentlessReport();
+ shareConsentDialog(ConsentReply.ALLOW);
+ assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue();
+ assertThat(bugreportFiles.getFirst().length()).isEqualTo(0);
+ waitForDumpstateServiceToStop();
+
+ // The retrieved last bugreport file should not be empty.
+ latch = new CountDownLatch(1);
+ callback = new BugreportCallbackImpl(latch);
+ mBugreportManager.retrieveBugreport(
+ bugreportFileLocations.getLast(), parcelFd(bugreportFiles.getLast()),
+ mContext.getMainExecutor(), callback);
+ ensureNotConsentlessReport();
+ shareConsentDialog(ConsentReply.ALLOW);
+ assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue();
+ assertThat(bugreportFiles.getLast().length()).isGreaterThan(0);
+ waitForDumpstateServiceToStop();
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS)
+ public void testBugreport_skipsConsentForDeferredReportAfterFullReport() throws Exception {
+ try {
+ ensureNotConsentlessReport();
+ startFullReport(false);
+
+ startDeferredReport(true);
+ startDeferredReport(true);
+
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS)
+ public void testBugreport_skipConsentForDeferredReportAfterDeferredReport() throws Exception {
+ try {
+ ensureNotConsentlessReport();
+ startDeferredReport(false);
+
+ startDeferredReport(true);
+
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS)
+ @Ignore("b/344704922")
+ public void testBugreport_doesNotSkipConsentForFullReportAfterFullReport() throws Exception {
+ try {
+ ensureNotConsentlessReport();
+ startFullReport(false);
+
+ startFullReport(false);
+
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS)
+ public void testBugreport_skipConsentForFullReportAfterDeferredReport() throws Exception {
+ try {
+ ensureNotConsentlessReport();
+ startDeferredReport(false);
+
+ startFullReport(true);
+
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS)
+ public void testBugreport_doesNotSkipConsentAfterTimeLimit() throws Exception {
+ try {
+ ensureNotConsentlessReport();
+ startFullReport(false);
+ final long newTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3);
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ mContext.getSystemService(AlarmManager.class).setTime(newTime));
+
+ startDeferredReport(false);
+
+ } finally {
+ waitForDumpstateServiceToStop();
+ // Remove all bugreport files
+ SystemUtil.runShellCommand("rm -f -rR -v /bugreports/");
+ }
+ }
+
+ private void ensureNotConsentlessReport() {
+ final long time = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(60);
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ mContext.getSystemService(AlarmManager.class).setTime(time));
+ assertThat(System.currentTimeMillis()).isGreaterThan(time);
+ }
+
+ private void startFullReport(boolean skipConsent) throws Exception {
+ waitForDumpstateServiceToStop();
+ File bugreportFile = createTempFile("startbugreport", ".zip");
CountDownLatch latch = new CountDownLatch(1);
BugreportCallbackImpl callback = new BugreportCallbackImpl(latch);
- mBugreportManager.startBugreport(parcelFd(new File("/dev/null")),
- null, new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE,
- BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT),
+ mBugreportManager.startBugreport(parcelFd(bugreportFile), null,
+ new BugreportParams(BugreportParams.BUGREPORT_MODE_ONBOARDING, 0),
mContext.getMainExecutor(), callback);
- latch.await(4, TimeUnit.MINUTES);
+ if (!skipConsent) {
+ shareConsentDialog(ConsentReply.ALLOW);
+ }
+
+ latch.await(2, TimeUnit.MINUTES);
assertThat(callback.isSuccess()).isTrue();
- String bugreportFileLocation = callback.getBugreportFile();
+ // No data should be passed to the FD used to call startBugreport.
+ assertThat(bugreportFile.length()).isGreaterThan(0);
+ waitForDumpstateServiceToStop();
+ }
+
+ private void startDeferredReport(boolean skipConsent) throws Exception {
+ waitForDumpstateServiceToStop();
+ File bugreportFile = createTempFile("startbugreport", ".zip");
+ CountDownLatch latch = new CountDownLatch(1);
+ BugreportCallbackImpl callback = new BugreportCallbackImpl(latch);
+ mBugreportManager.startBugreport(parcelFd(bugreportFile), null,
+ new BugreportParams(
+ BugreportParams.BUGREPORT_MODE_ONBOARDING,
+ BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT),
+ mContext.getMainExecutor(), callback);
+
+ latch.await(1, TimeUnit.MINUTES);
+ assertThat(callback.isSuccess()).isTrue();
+ String location = callback.getBugreportFile();
waitForDumpstateServiceToStop();
- latch = new CountDownLatch(1);
- ParcelFileDescriptor bugreportFd = parcelFd(bugreportFile);
- assertThat(bugreportFd).isNotNull();
- mBugreportManager.retrieveBugreport(
- bugreportFileLocation,
- bugreportFd,
- mContext.getMainExecutor(),
- callback);
- shareConsentDialog(ConsentReply.DENY);
- latch.await(1, TimeUnit.MINUTES);
- assertThat(callback.getErrorCode()).isEqualTo(
- BugreportCallback.BUGREPORT_ERROR_USER_DENIED_CONSENT);
- assertThat(bugreportFile.length()).isEqualTo(0);
- // Since consent has already been denied, this call should fail because consent cannot
- // be requested twice for the same bugreport.
+ // The retrieved bugreport file should not be empty.
latch = new CountDownLatch(1);
callback = new BugreportCallbackImpl(latch);
- mBugreportManager.retrieveBugreport(bugreportFileLocation, parcelFd(bugreportFile),
+ mBugreportManager.retrieveBugreport(
+ location, parcelFd(bugreportFile),
mContext.getMainExecutor(), callback);
- latch.await(1, TimeUnit.MINUTES);
- assertThat(callback.getErrorCode()).isEqualTo(
- BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
+ if (!skipConsent) {
+ shareConsentDialog(ConsentReply.ALLOW);
+ }
+ assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue();
+ assertThat(bugreportFile.length()).isGreaterThan(0);
+ waitForDumpstateServiceToStop();
+ }
+
+ private void triggerShellBugreport(int type) throws Exception {
+ BugreportBroadcastReceiver br = new BugreportBroadcastReceiver();
+ final IntentFilter intentFilter = new IntentFilter(INTENT_BUGREPORT_FINISHED);
+ mContext.registerReceiver(br, intentFilter, Context.RECEIVER_EXPORTED);
+ final BugreportParams params = new BugreportParams(type);
+ mBugreportManager.requestBugreport(params, "" /* shareTitle */, "" /* shareDescription */);
+
+ try {
+ br.waitForBugreportFinished();
+ } finally {
+ // The latch may fail for a number of reasons but we still need to unregister the
+ // BroadcastReceiver.
+ mContext.unregisterReceiver(br);
+ }
+
+ Intent response = br.getBugreportFinishedIntent();
+ assertThat(response.getAction()).isEqualTo(intentFilter.getAction(0));
+ waitForDumpstateServiceToStop();
+ }
+
+ private class BugreportBroadcastReceiver extends BroadcastReceiver {
+ Intent bugreportFinishedIntent = null;
+ final CountDownLatch latch;
+
+ BugreportBroadcastReceiver() {
+ latch = new CountDownLatch(1);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ setBugreportFinishedIntent(intent);
+ latch.countDown();
+ }
+
+ private void setBugreportFinishedIntent(Intent intent) {
+ bugreportFinishedIntent = intent;
+ }
+
+ public Intent getBugreportFinishedIntent() {
+ return bugreportFinishedIntent;
+ }
+
+ public void waitForBugreportFinished() throws Exception {
+ if (!latch.await(BUGREPORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new Exception("Failed to receive BUGREPORT_FINISHED in "
+ + BUGREPORT_TIMEOUT_MS + " ms.");
+ }
+ }
}
private ParcelFileDescriptor parcelFd(File file) throws Exception {
@@ -250,10 +571,6 @@
private void shareConsentDialog(@NonNull ConsentReply consentReply) throws Exception {
final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- // Unlock before finding/clicking an object.
- device.wakeUp();
- device.executeShellCommand("wm dismiss-keyguard");
-
final BySelector consentTitleObj = By.res("android", "alertTitle");
if (!device.wait(Until.hasObject(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)) {
fail("The consent dialog is not found");
@@ -274,20 +591,46 @@
assertThat(device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)).isTrue();
}
+ /*
+ * Ensure the consent dialog is shown and take action according to <code>consentReply<code/>.
+ * It will fail if the dialog is not shown when <code>ignoreNotFound<code/> is false.
+ */
+ private void ensureNoConsentDialogShown() throws Exception {
+ final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ final BySelector consentTitleObj = By.res("android", "alertTitle");
+ if (!device.wait(Until.hasObject(consentTitleObj), TimeUnit.SECONDS.toMillis(2))) {
+ return;
+ }
+ final BySelector selector = By.res("android", "button2");
+ final UiObject2 btnObj = device.findObject(selector);
+ if (btnObj == null) {
+ return;
+ }
+ btnObj.click();
+
+ device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS);
+ }
+
/** Waits for the dumpstate service to stop, for up to 5 seconds. */
private void waitForDumpstateServiceToStop() throws Exception {
int pollingIntervalMillis = 100;
- int numPolls = 50;
Method method = Class.forName("android.os.ServiceManager").getMethod(
"getService", String.class);
- while (numPolls-- > 0) {
- // If getService() returns null, the service has stopped.
- if (method.invoke(null, "dumpstate") == null) {
- return;
+ for (int i = 0; i < 10; i++) {
+ int numPolls = 50;
+ while (numPolls-- > 0) {
+ // If getService() returns null, the service has stopped.
+ if (method.invoke(null, "dumpstate") == null) {
+ break;
+ }
+ Thread.sleep(pollingIntervalMillis);
}
- Thread.sleep(pollingIntervalMillis);
}
- fail("Dumpstate did not stop within 5 seconds");
+ if (method.invoke(null, "dumpstate") == null) {
+ return;
+ }
+ fail("Dumpstate did not stop within 25 seconds");
}
}
diff --git a/tests/input/Android.bp b/tests/input/Android.bp
new file mode 100644
index 0000000..52c94d5
--- /dev/null
+++ b/tests/input/Android.bp
@@ -0,0 +1,57 @@
+// Copyright 2024 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 {
+ default_team: "trendy_team_input_framework",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsInputRootTestCases",
+ defaults: ["cts_defaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts_root",
+ "general-tests",
+ ],
+
+ compile_multilib: "both",
+ kotlincflags: [
+ "-Werror",
+ ],
+ srcs: [
+ "src/**/*.kt",
+ ],
+ asset_dirs: ["assets"],
+ static_libs: [
+ "cts-input-lib",
+ "CtsVirtualDeviceCommonLib",
+ "android.view.flags-aconfig-java",
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "bedstead-root-annotations",
+ "com.android.hardware.input-aconfig-java",
+ "com.android.input.flags-aconfig-java",
+ "compatibility-device-util-axt",
+ "cts-input-lib",
+ "cts-wm-util",
+ "flag-junit",
+ "kotlin-test",
+ "ui-trace-collector",
+ "collector-device-lib",
+ "platform-screenshot-diff-core",
+ ],
+ sdk_version: "test_current",
+ per_testcase_directory: true,
+}
diff --git a/tests/input/AndroidManifest.xml b/tests/input/AndroidManifest.xml
new file mode 100644
index 0000000..47b55be
--- /dev/null
+++ b/tests/input/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2024 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.input.cts_root">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+ <application android:label="InputTest"
+ android:requestLegacyExternalStorage="true">
+ <activity android:name="android.input.cts_root.CaptureEventActivity"
+ android:label="Capture events"
+ android:configChanges="touchscreen|uiMode|orientation|screenSize|screenLayout|keyboardHidden|uiMode|navigation|keyboard|density|fontScale|layoutDirection|locale|mcc|mnc|smallestScreenSize"
+ android:enableOnBackInvokedCallback="false"
+ android:turnScreenOn="true"
+ android:exported="true">
+ </activity>
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.input.cts_root"
+ android:label="Tests for input APIs and behaviours.">
+ </instrumentation>
+</manifest>
diff --git a/tests/input/AndroidTest.xml b/tests/input/AndroidTest.xml
new file mode 100644
index 0000000..fa5797e
--- /dev/null
+++ b/tests/input/AndroidTest.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 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.
+-->
+<configuration description="Config for CTS-root input test cases">
+ <option name="test-suite-tag" value="cts_root" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsInputRootTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.input.cts_root" />
+ <option name="runtime-hint" value="14s" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="test-timeout" value="600000" />
+ <option name="device-listeners" value="android.device.collectors.ScreenshotOnFailureCollector"/>
+ <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
+ <!-- DefaultUITraceListener args -->
+ <option name="instrumentation-arg" key="skip_test_success_metrics" value="true"/>
+ </test>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- TODO(b/285554134): Remove once underlying issue is fixed-->
+ <option name="run-command" value="wm set-ignore-orientation-request false" />
+ <option name="run-command" value="wm set-letterbox-style --isEducationEnabled false" />
+ <!-- Unlock screen -->
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <!-- Dismiss keyguard, in case it's set as "Swipe to unlock" -->
+ <option name="run-command" value="wm dismiss-keyguard" />
+ <!-- Collapse notifications -->
+ <option name="run-command" value="cmd statusbar collapse" />
+ <!-- dismiss all system dialogs before launch test -->
+ <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS" />
+ </target_preparer>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="input_.*" />
+ <!-- Pull perfetto traces from DefaultUITraceListener -->
+ <option name="pull-pattern-keys" value="perfetto_file_path*" />
+ <!-- Pull screenshot on test failure -->
+ <option name="pull-pattern-keys"
+ value="android.device.collectors.ScreenshotOnFailureCollector.*\.png" />
+ <!-- Pull files created by tests, like the output of screenshot tests -->
+ <option name="directory-keys" value="/sdcard/Download/CtsInputRootTestCases" />
+ <option name="collect-on-run-ended-only" value="false" />
+ </metrics_collector>
+</configuration>
diff --git a/tests/input/OWNERS b/tests/input/OWNERS
new file mode 100644
index 0000000..21d208f
--- /dev/null
+++ b/tests/input/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136048
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/tests/input/TEST_MAPPING b/tests/input/TEST_MAPPING
new file mode 100644
index 0000000..cb36f58
--- /dev/null
+++ b/tests/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "CtsInputRootTestCases"
+ }
+ ]
+}
diff --git a/tests/input/assets/testHidePointerIconOnSecureWindowScreenshot_DRAWING_TABLET_expected.png b/tests/input/assets/testHidePointerIconOnSecureWindowScreenshot_DRAWING_TABLET_expected.png
new file mode 100644
index 0000000..2774392
--- /dev/null
+++ b/tests/input/assets/testHidePointerIconOnSecureWindowScreenshot_DRAWING_TABLET_expected.png
Binary files differ
diff --git a/tests/input/assets/testHidePointerIconOnSecureWindowScreenshot_MOUSE_expected.png b/tests/input/assets/testHidePointerIconOnSecureWindowScreenshot_MOUSE_expected.png
new file mode 100644
index 0000000..2774392
--- /dev/null
+++ b/tests/input/assets/testHidePointerIconOnSecureWindowScreenshot_MOUSE_expected.png
Binary files differ
diff --git a/tests/input/src/android/input/cts_root/CaptureEventActivity.kt b/tests/input/src/android/input/cts_root/CaptureEventActivity.kt
new file mode 100644
index 0000000..246e6af
--- /dev/null
+++ b/tests/input/src/android/input/cts_root/CaptureEventActivity.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 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.input.cts_root
+
+import android.app.Activity
+import android.view.InputEvent
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+
+class CaptureEventActivity : Activity() {
+ private val events = LinkedBlockingQueue<InputEvent>()
+
+ override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
+ events.add(MotionEvent.obtain(ev))
+ return true
+ }
+
+ override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
+ events.add(MotionEvent.obtain(ev))
+ return true
+ }
+
+ override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
+ events.add(KeyEvent(event))
+ return true
+ }
+
+ override fun dispatchTrackballEvent(ev: MotionEvent?): Boolean {
+ events.add(MotionEvent.obtain(ev))
+ return true
+ }
+
+ fun getInputEvent(): InputEvent? {
+ return events.poll(5, TimeUnit.SECONDS)
+ }
+}
diff --git a/tests/input/src/android/input/cts_root/HidePointerIconOnSecureWindowScreenshotTest.kt b/tests/input/src/android/input/cts_root/HidePointerIconOnSecureWindowScreenshotTest.kt
new file mode 100644
index 0000000..471084b
--- /dev/null
+++ b/tests/input/src/android/input/cts_root/HidePointerIconOnSecureWindowScreenshotTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2024 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.input.cts_root
+
+import android.cts.input.EventVerifier
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.os.SystemProperties
+import android.platform.test.annotations.EnableFlags
+import android.view.MotionEvent
+import android.view.WindowManager
+import android.virtualdevice.cts.common.FakeAssociationRule
+import androidx.test.filters.MediumTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.cts.input.DefaultPointerSpeedRule
+import com.android.cts.input.TestPointerDevice
+import com.android.cts.input.VirtualDisplayActivityScenario
+import com.android.cts.input.inputeventmatchers.withMotionAction
+import com.android.input.flags.Flags
+import com.android.xts.root.annotations.RequireAdbRoot
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameter
+import platform.test.screenshot.GoldenPathManager
+import platform.test.screenshot.PathConfig
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.assertAgainstGolden
+import platform.test.screenshot.matchers.AlmostPerfectMatcher
+import platform.test.screenshot.matchers.BitmapMatcher
+import kotlin.test.assertNotNull
+import org.junit.Ignore
+
+/**
+ * End-to-end tests for the hiding pointer icons of screenshots of secure displays
+ *
+ * We use a secure virtual display to launch the test activity, and use virtual Input devices to
+ * move the pointer for it to show up. We then take a screenshot of the display to ensure the icon
+ * does not shows up on screenshot. We use the virtual display to be able to precisely compare the
+ * screenshots across devices of various form factors and sizes.
+ *
+ * Following tests must be run as root as they require CAPTURE_SECURE_VIDEO_OUTPUT permission
+ * override which can only be done by root.
+ */
+@MediumTest
+@RunWith(Parameterized::class)
+@RequireAdbRoot
+class HidePointerIconOnSecureWindowScreenshotTest {
+ private lateinit var activity: CaptureEventActivity
+ private lateinit var verifier: EventVerifier
+ private lateinit var exactScreenshotMatcher: BitmapMatcher
+
+ @get:Rule
+ val testName = TestName()
+ @get:Rule
+ val virtualDisplayRule = VirtualDisplayActivityScenario.Rule<CaptureEventActivity>(
+ testName,
+ useSecureDisplay = true,
+ )
+ @get:Rule
+ val fakeAssociationRule = FakeAssociationRule()
+ @get:Rule
+ val defaultPointerSpeedRule = DefaultPointerSpeedRule()
+ @get:Rule
+ val screenshotRule = ScreenshotTestRule(GoldenPathManager(
+ InstrumentationRegistry.getInstrumentation().context,
+ ASSETS_PATH,
+ TEST_OUTPUT_PATH,
+ PathConfig()
+ ), disableIconPool = false)
+
+ @Parameter(0)
+ lateinit var device: TestPointerDevice
+
+ @Before
+ fun setUp() {
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
+ activity = virtualDisplayRule.activity
+ activity.runOnUiThread {
+ activity.actionBar?.hide()
+ activity.window.decorView.rootView.setBackgroundColor(Color.WHITE)
+ activity.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+ }
+
+ device.setUp(
+ context,
+ virtualDisplayRule.virtualDisplay.display,
+ fakeAssociationRule.associationInfo,
+ )
+
+ verifier = EventVerifier(activity::getInputEvent)
+
+ exactScreenshotMatcher =
+ AlmostPerfectMatcher(acceptableThresholdCount = MAX_PIXELS_DIFFERENT)
+ }
+
+ @After
+ fun tearDown() {
+ device.tearDown()
+ }
+
+ @Ignore("b/366475909")
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_POINTER_INDICATORS_FOR_SECURE_WINDOWS)
+ fun testHidePointerIconOnSecureWindowScreenshot() {
+ device.hoverMove(1, 1)
+ verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_HOVER_ENTER))
+ waitForPointerIconUpdate()
+
+ assertScreenshotsMatch()
+ }
+
+ private fun getActualScreenshot(): Bitmap {
+ val actualBitmap: Bitmap? = virtualDisplayRule.getScreenshot()
+ assertNotNull(actualBitmap, "Screenshot is null.")
+ return actualBitmap
+ }
+
+ private fun assertScreenshotsMatch() {
+ getActualScreenshot().assertAgainstGolden(
+ screenshotRule,
+ getParameterizedExpectedScreenshotName(),
+ exactScreenshotMatcher
+ )
+ }
+
+ private fun getParameterizedExpectedScreenshotName(): String {
+ // Replace illegal characters '[' and ']' in expected screenshot name with underscores.
+ return "${testName.methodName}expected".replace("""\[|\]""".toRegex(), "_")
+ }
+
+ // We don't have a way to synchronously know when the requested pointer icon has been drawn
+ // to the display, so wait some time (at least one display frame) for the icon to propagate.
+ private fun waitForPointerIconUpdate() = Thread.sleep(500L * HW_TIMEOUT_MULTIPLIER)
+
+ companion object {
+ const val MAX_PIXELS_DIFFERENT = 5
+ const val ASSETS_PATH = "tests/input/assets"
+ val TEST_OUTPUT_PATH =
+ "/sdcard/Download/CtsInputRootTestCases/" +
+ HidePointerIconOnSecureWindowScreenshotTest::class.java.simpleName
+ val HW_TIMEOUT_MULTIPLIER = SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data(): Iterable<Any> =
+ listOf(TestPointerDevice.MOUSE, TestPointerDevice.DRAWING_TABLET)
+ }
+}
diff --git a/tests/packagemanagerlocal/Android.bp b/tests/packagemanagerlocal/Android.bp
index a1c58e8..0202f4e 100644
--- a/tests/packagemanagerlocal/Android.bp
+++ b/tests/packagemanagerlocal/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -27,8 +28,8 @@
"truth",
],
libs: [
- "android.test.runner",
- "android.test.base",
+ "android.test.runner.stubs.test",
+ "android.test.base.stubs.test",
],
srcs: ["src/**/*.java"],
test_suites: [
diff --git a/tests/packagemanagerlocal/OWNERS b/tests/packagemanagerlocal/OWNERS
index 4e5fe55..54f11a0 100644
--- a/tests/packagemanagerlocal/OWNERS
+++ b/tests/packagemanagerlocal/OWNERS
@@ -1,3 +1,2 @@
# Bug component: 36137
-alexbuy@google.com
patb@google.com
diff --git a/tests/packagewatchdog/Android.bp b/tests/packagewatchdog/Android.bp
index 5c65280..6bc2d6c 100644
--- a/tests/packagewatchdog/Android.bp
+++ b/tests/packagewatchdog/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_mainline_modularization",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -27,8 +28,8 @@
"platform-test-annotations",
],
libs: [
- "android.test.runner",
- "android.test.base",
+ "android.test.runner.stubs.test",
+ "android.test.base.stubs.test",
],
srcs: ["src/**/*.java"],
test_suites: [
diff --git a/tests/packagewatchdog/OWNERS b/tests/packagewatchdog/OWNERS
index b929d27..86b12cd 100644
--- a/tests/packagewatchdog/OWNERS
+++ b/tests/packagewatchdog/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 1306443
-ancr@google.com
harshitmahajan@google.com
wangchun@google.com
\ No newline at end of file
diff --git a/tests/packagewatchdog/src/android/packagewatchdog/cts_root/PackageWatchdogTest.java b/tests/packagewatchdog/src/android/packagewatchdog/cts_root/PackageWatchdogTest.java
index d8b9b42..503b5c0 100644
--- a/tests/packagewatchdog/src/android/packagewatchdog/cts_root/PackageWatchdogTest.java
+++ b/tests/packagewatchdog/src/android/packagewatchdog/cts_root/PackageWatchdogTest.java
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package android.packagewatchdog.cts_root;
+package android.packagewatchdog.cts;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.pm.VersionedPackage;
+import android.test.UiThreadTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -52,6 +53,7 @@
private TestObserver mTestObserver1, mTestObserver2;
@Before
+ @UiThreadTest
public void setUp() {
Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
mPackageWatchdog = PackageWatchdog.getInstance(mContext);
@@ -178,9 +180,9 @@
mPackageWatchdog.startObservingHealth(mTestObserver1, Arrays.asList(APP_A), SHORT_DURATION);
- mPackageWatchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
+ raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
- mPackageWatchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
+ raiseFatalFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)),
PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
assertThat(mLatch1.await(5, TimeUnit.SECONDS)).isTrue();
@@ -237,6 +239,12 @@
for (int i = 0; i < failureCount; i++) {
mPackageWatchdog.onPackageFailure(failingPackages, failureReason);
}
+ try {
+ // Wait for DEFAULT_MITIGATION_WINDOW_MS before applying another mitigation
+ Thread.sleep(TimeUnit.SECONDS.toMillis(6));
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
private static class TestObserver implements PackageWatchdog.PackageHealthObserver {
@@ -273,7 +281,7 @@
return true;
}
- public String getName() {
+ public String getUniqueIdentifier() {
return mName;
}
diff --git a/tests/permission/Android.bp b/tests/permission/Android.bp
new file mode 100644
index 0000000..fd69606
--- /dev/null
+++ b/tests/permission/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2024 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+ default_team: "trendy_team_android_permissions",
+}
+
+android_test {
+ name: "CtsRootPermissionTestCases",
+ defaults: ["cts_defaults"],
+ data: [
+ ":CtsRootPermissionSignaturePermissionAllowlistNormalApp",
+ ],
+ libs: [
+ "android.test.runner.stubs.system",
+ "android.test.base.stubs.system",
+ "bedstead-root-annotations",
+ ],
+ min_sdk_version: "30",
+ // TODO(b/326241209): @TestApi isn't supported in system server right now.
+ //sdk_version: "test_current",
+ platform_apis: true,
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "flag-junit",
+ "platform-test-annotations",
+ "services.core",
+ "truth",
+ ],
+ target_sdk_version: "35",
+ test_suites: [
+ "cts_root",
+ // For gts-root before cts-root is required.
+ "gts",
+ "general-tests",
+ ],
+}
diff --git a/tests/permission/AndroidManifest.xml b/tests/permission/AndroidManifest.xml
new file mode 100644
index 0000000..514b34c
--- /dev/null
+++ b/tests/permission/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2024 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.permission.cts_root" >
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android"
+ android:label="Permission CTS root tests" />
+</manifest>
diff --git a/tests/permission/AndroidTest.xml b/tests/permission/AndroidTest.xml
new file mode 100644
index 0000000..5ce9d32
--- /dev/null
+++ b/tests/permission/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<configuration description="Runs Permission CTS root tests">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="test-suite-tag" value="gts" />
+ <option name="config-descriptor:metadata" key="component" value="gts-root"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="CtsRootPermissionSignaturePermissionAllowlistNormalApp.apk" value="/data/local/tmp/cts-root-permission/CtsRootPermissionSignaturePermissionAllowlistNormalApp.apk" />
+ <option name="cleanup" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsRootPermissionTestCases.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.permission.cts_root" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="restart" value="false" />
+ </test>
+</configuration>
diff --git a/tests/permission/OWNERS b/tests/permission/OWNERS
new file mode 100644
index 0000000..fb6099c
--- /dev/null
+++ b/tests/permission/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/permission/apps/CtsRootPermissionSignaturePermissionAllowlistNormalApp/Android.bp b/tests/permission/apps/CtsRootPermissionSignaturePermissionAllowlistNormalApp/Android.bp
new file mode 100644
index 0000000..884d3f1
--- /dev/null
+++ b/tests/permission/apps/CtsRootPermissionSignaturePermissionAllowlistNormalApp/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsRootPermissionSignaturePermissionAllowlistNormalApp",
+ min_sdk_version: "30",
+ target_sdk_version: "35",
+}
diff --git a/tests/permission/apps/CtsRootPermissionSignaturePermissionAllowlistNormalApp/AndroidManifest.xml b/tests/permission/apps/CtsRootPermissionSignaturePermissionAllowlistNormalApp/AndroidManifest.xml
new file mode 100644
index 0000000..6a6ec99
--- /dev/null
+++ b/tests/permission/apps/CtsRootPermissionSignaturePermissionAllowlistNormalApp/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2024 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.permission.cts_root.apps.signaturepermissionallowlist.normal">
+
+ <uses-permission android:name="android.permission.BRICK" />
+ <uses-permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE" />
+
+ <application
+ android:hasCode="false"
+ android:label="CtsRootPermissionSignaturePermissionAllowlistNormalApp" />
+</manifest>
diff --git a/tests/permission/src/android/permission/cts-root/SignaturePermissionAllowlistTest.java b/tests/permission/src/android/permission/cts-root/SignaturePermissionAllowlistTest.java
new file mode 100644
index 0000000..10b3546
--- /dev/null
+++ b/tests/permission/src/android/permission/cts-root/SignaturePermissionAllowlistTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 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.permission.cts_root;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.SigningDetails;
+import android.os.Build;
+import android.permission.flags.Flags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.permission.PermissionManagerLocal;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.PackageState;
+import com.android.xts.root.annotations.RequireAdbRoot;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+@RequireAdbRoot
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream")
+public final class SignaturePermissionAllowlistTest {
+ private static final String NORMAL_APP_APK_PATH = "/data/local/tmp/cts-root-permission/"
+ + "CtsRootPermissionSignaturePermissionAllowlistNormalApp.apk";
+ private static final String NORMAL_APP_PACKAGE_NAME =
+ "android.permission.cts_root.apps.signaturepermissionallowlist.normal";
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @NonNull
+ private final PackageManagerLocal mPackageManagerLocal =
+ LocalManagerRegistry.getManager(PackageManagerLocal.class);
+ @NonNull
+ private final PermissionManagerLocal mPermissionManagerLocal =
+ LocalManagerRegistry.getManager(PermissionManagerLocal.class);
+ @NonNull
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ @NonNull
+ private final PackageManager mPackageManager = mContext.getPackageManager();
+
+ @NonNull
+ private SigningDetails mNormalAppSigningDetails;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ assumeTrue(Build.isDebuggable());
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mPermissionManagerLocal.setSignaturePermissionAllowlistForceEnforced(true);
+ installPackage(NORMAL_APP_APK_PATH);
+ SigningDetails platformSigningDetails;
+ try (var snapshot = mPackageManagerLocal.withUnfilteredSnapshot()) {
+ Map<String, PackageState> packageStates = snapshot.getPackageStates();
+ mNormalAppSigningDetails = packageStates.get(NORMAL_APP_PACKAGE_NAME)
+ .getAndroidPackage().getSigningDetails();
+ platformSigningDetails = packageStates.get("android").getAndroidPackage()
+ .getSigningDetails();
+ }
+ uninstallPackage(NORMAL_APP_PACKAGE_NAME);
+ mPackageManagerLocal.addOverrideSigningDetails(mNormalAppSigningDetails,
+ platformSigningDetails);
+ installPackage(NORMAL_APP_APK_PATH);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallPackage(NORMAL_APP_PACKAGE_NAME);
+ mPackageManagerLocal.removeOverrideSigningDetails(mNormalAppSigningDetails);
+ mPermissionManagerLocal.setSignaturePermissionAllowlistForceEnforced(false);
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_SIGNATURE_PERMISSION_ALLOWLIST_ENABLED)
+ @Test
+ public void normalAppCanNotGetSignaturePermissionWithoutAllowlist() throws Exception {
+ assertThat(mPackageManager.checkPermission(android.Manifest.permission.BRICK,
+ NORMAL_APP_PACKAGE_NAME)).isEqualTo(PackageManager.PERMISSION_DENIED);
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_SIGNATURE_PERMISSION_ALLOWLIST_ENABLED)
+ @Test
+ public void normalAppCanGetSignaturePermissionWithAllowlist() throws Exception {
+ assertThat(mPackageManager.checkPermission(
+ android.Manifest.permission.RESERVED_FOR_TESTING_SIGNATURE,
+ NORMAL_APP_PACKAGE_NAME)).isEqualTo(PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void installPackage(@NonNull String apkPath) throws Exception {
+ SystemUtil.runShellCommandOrThrow("pm install " + apkPath);
+ }
+
+ private void uninstallPackage(@NonNull String packageName) throws Exception {
+ SystemUtil.runShellCommandOrThrow("pm uninstall " + packageName);
+ }
+}
diff --git a/tests/stats/Android.bp b/tests/stats/Android.bp
index 2b76c6a..2567075 100644
--- a/tests/stats/Android.bp
+++ b/tests/stats/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_telemetry_client_infra",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -34,8 +35,8 @@
"platform-test-annotations",
],
libs: [
- "android.test.runner",
- "android.test.base",
+ "android.test.runner.stubs.test",
+ "android.test.base.stubs.test",
],
srcs: ["src/**/*.java"],
test_suites: [
diff --git a/tests/usage/Android.bp b/tests/usage/Android.bp
index 5936bc4..7bd028b 100644
--- a/tests/usage/Android.bp
+++ b/tests/usage/Android.bp
@@ -28,8 +28,8 @@
"truth",
],
libs: [
- "android.test.runner",
- "android.test.base",
+ "android.test.runner.stubs.test",
+ "android.test.base.stubs.test",
"services.core",
"services.usage",
],