diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-11-04 00:16:33 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-11-04 00:16:33 +0000 |
commit | 25e75775d8a849f2f1dd0e4204419721b9a4e55e (patch) | |
tree | c95cc031fcd4b425a856806581313bc1eed3cf96 | |
parent | b56382f891a5b1d2c7e9a8c13e3ab1071dbdc04b (diff) | |
parent | 61669df01b1d38acb204585f919ccd7faac4c711 (diff) | |
download | cts-android13-mainline-ipsec-release.tar.gz |
Snap for 9254005 from 61669df01b1d38acb204585f919ccd7faac4c711 to mainline-ipsec-releaseaml_ips_331910010aml_ips_331312000aml_ips_331310000android13-mainline-ipsec-release
Change-Id: Ic6cb2c6d44e0f4fc4045d8d57240bb8d7ae99889
166 files changed, 5627 insertions, 1641 deletions
diff --git a/apps/PermissionApp/Android.bp b/apps/PermissionApp/Android.bp index 70c7d672b0c..df62a77e5fa 100644 --- a/apps/PermissionApp/Android.bp +++ b/apps/PermissionApp/Android.bp @@ -30,6 +30,6 @@ android_test_helper_app { "arcts", "cts", "general-tests", - "mts", + "mts-permission", ], } diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java index f8b24fdd4d9..cdec8961275 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java @@ -481,7 +481,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic Context context = getInstrumentation().getTargetContext(); for (PackageInfo pkg : packageInfos.values()) { - int targetSdk = pkg.applicationInfo.targetSandboxVersion; + int targetSdk = pkg.applicationInfo.targetSdkVersion; int uid = pkg.applicationInfo.uid; for (String permission : pkg.requestedPermissions) { @@ -571,7 +571,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } for (String requestedPermission : packageInfo.requestedPermissions) { if (pregrantedPerms.contains(requestedPermission)) { - uidState.addGrantedPermission(packageInfo.packageName, reason, requestedPermission, + uidState.addGrantedPermission(packageInfo, reason, requestedPermission, fixed); } } @@ -815,22 +815,16 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } } - public void addGrantedPermission(String packageName, String reason, String permission, + public void addGrantedPermission(PackageInfo packageInfo, String reason, String permission, FixedState fixed) { - Context context = getInstrumentation().getTargetContext(); + String packageName = packageInfo.packageName; + int targetSdk = packageInfo.applicationInfo.targetSdkVersion; // Add permissions split off from the permission to granted - try { - PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); - int targetSdk = info.applicationInfo.targetSdkVersion; - - for (String extendedPerm : extendBySplitPermissions(permission, targetSdk)) { - mergeGrantedPermission(packageName, extendedPerm.equals(permission) ? reason - : reason + " (split from " + permission + ")", extendedPerm, - fixed, false); - } - } catch (PackageManager.NameNotFoundException e) { - // ignore + for (String extendedPerm : extendBySplitPermissions(permission, targetSdk)) { + mergeGrantedPermission(packageName, extendedPerm.equals(permission) ? reason + : reason + " (split from " + permission + ")", extendedPerm, + fixed, false); } } diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java index 488c73faa09..c3b6900c64e 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaUtils.java @@ -1532,6 +1532,27 @@ public class MediaUtils { return false; } + /** + * Function to identify if the device is a cuttlefish instance + */ + public static boolean onCuttlefish() throws IOException { + String device = SystemProperties.get("ro.product.device", ""); + String model = SystemProperties.get("ro.product.model", ""); + String name = SystemProperties.get("ro.product.name", ""); + + // Return true for cuttlefish instances + if (!device.startsWith("vsoc_")) { + return false; + } + if (!model.startsWith("Cuttlefish ")) { + return false; + } + if (name.startsWith("cf_") || name.startsWith("aosp_cf_")) { + return true; + } + return false; + } + /* * -------------------------------------- END -------------------------------------- */ diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java index 3f42e32f5f5..a9543c246ab 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull; import android.graphics.Rect; import android.support.test.uiautomator.By; import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.StaleObjectException; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObjectNotFoundException; @@ -28,6 +29,7 @@ import android.support.test.uiautomator.UiScrollable; import android.support.test.uiautomator.UiSelector; import android.support.test.uiautomator.Until; import android.util.TypedValue; +import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.core.app.ApplicationProvider; @@ -37,6 +39,8 @@ import java.util.regex.Pattern; public class UiAutomatorUtils { private UiAutomatorUtils() {} + private static final String LOG_TAG = "UiAutomatorUtils"; + /** Default swipe deadzone percentage. See {@link UiScrollable}. */ private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1; @@ -86,7 +90,15 @@ public class UiAutomatorUtils { final int minViewHeightPx = convertDpToPx(MIN_VIEW_HEIGHT_DP); while (view == null && start + timeoutMs > System.currentTimeMillis()) { - view = getUiDevice().wait(Until.findObject(selector), 1000); + try { + view = getUiDevice().wait(Until.findObject(selector), 1000); + } catch (StaleObjectException exception) { + // UiDevice.wait() may cause StaleObjectException if the {@link View} attached to + // UiObject2 is no longer in the view tree. + Log.v(LOG_TAG, "UiObject2 view is no longer in the view tree.", exception); + getUiDevice().waitForIdle(); + continue; + } if (view == null || view.getVisibleBounds().height() < minViewHeightPx) { final double deadZone = !(FeatureUtil.isWatch() || FeatureUtil.isTV()) diff --git a/hostsidetests/appcompat/strictjavapackages/Android.bp b/hostsidetests/appcompat/strictjavapackages/Android.bp index 272be12119d..2656bbc1363 100644 --- a/hostsidetests/appcompat/strictjavapackages/Android.bp +++ b/hostsidetests/appcompat/strictjavapackages/Android.bp @@ -27,6 +27,7 @@ java_test_host { ], static_libs: [ "compat-classpaths-testing", + "modules-utils-build-testing", "dexlib2-no-guava-no-cli", ], // tag this module as a cts test artifact diff --git a/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml b/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml index e61058125cb..f76eab4bc9b 100644 --- a/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml +++ b/hostsidetests/appcompat/strictjavapackages/app/AndroidManifest.xml @@ -20,6 +20,7 @@ android:targetSandboxVersion="2"> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:requestLegacyExternalStorage="true"> <uses-library android:name="android.test.runner" /> diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java index da41f95ef30..6b1f6fc33b7 100644 --- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java +++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java @@ -236,6 +236,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { "Landroid/app/sdksandbox/ILoadSdkCallback;", "Landroid/app/sdksandbox/IRequestSurfacePackageCallback;", "Landroid/app/sdksandbox/ISdkSandboxManager;", + "Landroid/app/sdksandbox/ISdkSandboxLifecycleCallback;", "Landroid/app/sdksandbox/ISdkSandboxProcessDeathCallback;", "Landroid/app/sdksandbox/ISendDataCallback;", "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;", @@ -737,6 +738,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { "Lcom/android/sdksandbox/IDataReceivedCallback;", "Lcom/android/sdksandbox/ILoadSdkInSandboxCallback;", "Lcom/android/sdksandbox/IRequestSurfacePackageFromSdkCallback;", + "Lcom/android/sdksandbox/ISdkSandboxDisabledCallback;", "Lcom/android/sdksandbox/ISdkSandboxManagerToSdkSandboxCallback;", "Lcom/android/sdksandbox/ISdkSandboxService;", "Lcom/android/sdksandbox/SandboxLatencyInfo-IA;", @@ -1008,13 +1010,14 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { /** * Ensure that no apk-in-apex bundles classes that could be eclipsed by jars in - * BOOTCLASSPATH, SYSTEMSERVERCLASSPATH. + * BOOTCLASSPATH. */ @Test public void testApkInApex_nonClasspathClasses() throws Exception { HashMultimap<String, Multimap<String, String>> perApkClasspathDuplicates = HashMultimap.create(); Arrays.stream(collectApkInApexPaths()) + .filter(apk -> apk != null && !apk.isEmpty()) .parallel() .forEach(apk -> { File apkFile = null; @@ -1038,9 +1041,12 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { className -> !burndownClasses.contains(className) // TODO: b/225341497 && !className.equals("Landroidx/annotation/Keep;")); + final Multimap<String, String> bcpOnlyDuplicates = + Multimaps.filterKeys(filteredDuplicates, + sBootclasspathJars::contains); if (!filteredDuplicates.isEmpty()) { synchronized (perApkClasspathDuplicates) { - perApkClasspathDuplicates.put(apk, filteredDuplicates); + perApkClasspathDuplicates.put(apk, bcpOnlyDuplicates); } } } catch (Exception e) { @@ -1082,6 +1088,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { + "bootclasspath. Please use alternatives provided by the platform instead. " + "See go/androidx-api-guidelines#module-naming.") .that(sJarsToClasses.entries().stream() + .filter(e -> e.getKey().endsWith(".jar")) .filter(e -> e.getValue().startsWith("Landroidx/")) .filter(e -> !isLegacyAndroidxDependency( LegacyExemptAndroidxSharedLibsNamesToClasses, e.getKey(), e.getValue())) diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp index ed22bab07b6..d6b736d5198 100644 --- a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp +++ b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp @@ -38,7 +38,9 @@ android_test_helper_app { test_suites: [ "cts", "general-tests", - "mts", + "mts-documentsui", + "mts-mainline-infra", + "mts-mediaprovider", "sts", ], certificate: ":cts-testkey2", diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp index b42c9ac5d8c..723d0632057 100644 --- a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp +++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp @@ -33,7 +33,9 @@ android_test_helper_app { test_suites: [ "cts", "general-tests", - "mts", + "mts-documentsui", + "mts-mainline-infra", + "mts-mediaprovider", "sts", ], certificate: ":cts-testkey1", diff --git a/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp b/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp index 8c59b536a96..1608365815c 100644 --- a/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp +++ b/hostsidetests/inputmethodservice/deviceside/ime1/Android.bp @@ -28,6 +28,7 @@ android_test_helper_app { test_suites: [ "cts", "general-tests", + "sts", ], sdk_version: "test_current", min_sdk_version: "19", diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp index 0e2a887bc40..d5052b903db 100644 --- a/hostsidetests/scopedstorage/Android.bp +++ b/hostsidetests/scopedstorage/Android.bp @@ -238,6 +238,29 @@ android_test_helper_app { } android_test_helper_app { + name: "AppCloningDeviceTest", + manifest: "AndroidManifest.xml", + srcs: ["src/**/*.java"], + static_libs: [ + "truth-prebuilt", + "cts-scopedstorage-lib", + "modules-utils-build_system", + ], + compile_multilib: "both", + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], + sdk_version: "test_current", + target_sdk_version: "33", + min_sdk_version: "29", + java_resources: [ + ":CtsScopedStorageTestAppB", + ], +} + +android_test_helper_app { name: "LegacyStorageTest", manifest: "legacy/AndroidManifest.xml", srcs: ["legacy/src/**/*.java"], @@ -318,6 +341,31 @@ java_test_host { } java_test_host { + name: "CtsAppCloningMediaProviderHostTest", + srcs: ["host/src/**/*.java"], + libs: [ + "cts-tradefed", + "tradefed", + "testng", + ], + static_libs: [ + "modules-utils-build-testing", + "compatibility-host-util", + ], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], + test_config: "AndroidTestAppCloning.xml", + per_testcase_directory: true, + data: [ + ":CtsScopedStorageTestAppB", + ":AppCloningDeviceTest", + ], +} + +java_test_host { name: "GtsPreserveLegacyStorageHostTest", srcs: [ "host/src/android/scopedstorage/cts/host/PreserveLegacyStorageHostTest.java", diff --git a/hostsidetests/scopedstorage/AndroidTestAppCloning.xml b/hostsidetests/scopedstorage/AndroidTestAppCloning.xml new file mode 100644 index 00000000000..665db66947f --- /dev/null +++ b/hostsidetests/scopedstorage/AndroidTestAppCloning.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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="External storage host test Test for App cloning use case"> + <option name="test-suite-tag" value="cts" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <option + name="config-descriptor:metadata" + key="mainline-param" + value="com.google.android.mediaprovider.apex" /> + + <test class="com.android.tradefed.testtype.HostTest" > + <option name="class" value="android.scopedstorage.cts.host.AppCloningMediaProviderHostTest" /> + </test> + + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.mediaprovider" /> + </object> +</configuration> diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningMediaProviderHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningMediaProviderHostTest.java new file mode 100644 index 00000000000..77086f0d356 --- /dev/null +++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningMediaProviderHostTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2022 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.scopedstorage.cts.host; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.AppModeFull; + +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.invoker.TestInformation; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.AfterClassWithInfo; +import com.android.tradefed.testtype.junit4.BeforeClassWithInfo; +import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; +import com.android.tradefed.util.CommandResult; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; + +@RunWith(DeviceJUnit4ClassRunner.class) +@AppModeFull +public class AppCloningMediaProviderHostTest extends BaseHostTestCase{ + + protected static final String DEVICE_TEST_APP_PACKAGE = "android.scopedstorage.cts"; + protected static final String DEVICE_TEST_APP = "AppCloningDeviceTest.apk"; + private static final String DEVICE_TEST_CLASS = DEVICE_TEST_APP_PACKAGE + + ".AppCloningDeviceTest"; + // This app performs the File Creation and Read operations from the Device. + protected static final String SCOPED_STORAGE_TEST_APP_B_APK = "CtsScopedStorageTestAppB.apk"; + + private static final int CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS = 20000; + private static final long DEFAULT_INSTRUMENTATION_TIMEOUT_MS = 600_000; + private static final String EXTERNAL_STORAGE_PATH = "/storage/emulated/%d/"; + private static final String CURRENT_USER_ID = "currentUserId"; + private static final String FILE_TO_BE_CREATED = "fileToBeCreated"; + private static final String FILE_EXPECTED_TO_BE_PRESENT = "fileExpectedToBePresent"; + private static final String FILE_NOT_EXPECTED_TO_BE_PRESENT = "fileNotExpectedToBePresent"; + /** + * Provide different name to Files being created, on each execution of the test, so that + * flakiness from previously existing files can be avoided. + */ + private static final String NONCE = String.valueOf(System.nanoTime()); + private static String sCloneUserId; + + @BeforeClassWithInfo + public static void beforeClassWithDevice(TestInformation testInfo) throws Exception { + final ITestDevice sDevice = testInfo.getDevice(); + assertThat(sDevice).isNotNull(); + + setDevice(sDevice); + + assumeTrue("Device doesn't support multiple users", supportsMultipleUsers()); + assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode()); + assumeTrue(isAtLeastS()); + assumeFalse("Device uses sdcardfs", usesSdcardFs()); + + // create clone user + String output = sDevice.executeShellCommand( + "pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE " + + "testUser"); + sCloneUserId = output.substring(output.lastIndexOf(' ') + 1).replaceAll("[^0-9]", + ""); + assertThat(sCloneUserId).isNotEmpty(); + // start clone user + CommandResult out = sDevice.executeShellV2Command("am start-user -w " + sCloneUserId); + assertThat(isSuccessful(out)).isTrue(); + + Integer mCloneUserIdInt = Integer.parseInt(sCloneUserId); + String sCloneUserStoragePath = String.format(EXTERNAL_STORAGE_PATH, + Integer.parseInt(sCloneUserId)); + // Check that the clone user directories have been created + eventually(() -> sDevice.doesFileExist(sCloneUserStoragePath, mCloneUserIdInt), + CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS); + } + + @AfterClassWithInfo + public static void afterClass(TestInformation testInfo) throws Exception { + if (!supportsMultipleUsers() || isHeadlessSystemUserMode() || !isAtLeastS() + || usesSdcardFs()) return; + testInfo.getDevice().executeShellCommand("pm remove-user " + sCloneUserId); + } + + @Test + public void testGetFilesInDirectoryViaMediaProvider() throws Exception { + // Install the Device Test App in both the user spaces. + installPackage(DEVICE_TEST_APP, "--user all"); + // Install the Scoped Storage Test App in both the user spaces. + installPackage(SCOPED_STORAGE_TEST_APP_B_APK, "--user all"); + + int currentUserId = getCurrentUserId(); + final String fileName = "tmpFileToPush" + NONCE + ".png"; + + // We add the file in DCIM directory of User 0. + Map<String, String> ownerArgs = new HashMap<>(); + ownerArgs.put(CURRENT_USER_ID, String.valueOf(currentUserId)); + ownerArgs.put(FILE_TO_BE_CREATED, fileName); + runDeviceTestAsUserInPkgA("testInsertFilesInDirectoryViaMediaProvider", + currentUserId, ownerArgs); + + // We add the file in DCIM directory of Cloned User. + final String fileNameClonedUser = "tmpFileToPushClonedUser" + NONCE + ".png"; + Map<String, String> cloneArgs = new HashMap<>(); + cloneArgs.put(CURRENT_USER_ID, sCloneUserId); + cloneArgs.put(FILE_TO_BE_CREATED, fileNameClonedUser); + runDeviceTestAsUserInPkgA("testInsertFilesInDirectoryViaMediaProvider", + Integer.parseInt(sCloneUserId), cloneArgs); + + // Querying as user 0 should enlist the file(s) created by user 0 only. + Map<String, String> listFilesArgs = new HashMap<>(); + listFilesArgs.put(CURRENT_USER_ID, String.valueOf(currentUserId)); + listFilesArgs.put(FILE_EXPECTED_TO_BE_PRESENT, fileName); + listFilesArgs.put(FILE_NOT_EXPECTED_TO_BE_PRESENT, fileNameClonedUser); + runDeviceTestAsUserInPkgA("testGetFilesInDirectoryViaMediaProviderRespectsUserId", + currentUserId, listFilesArgs); + + // Querying as cloned user should enlist the file(s) created by cloned user only. + listFilesArgs.put(CURRENT_USER_ID, sCloneUserId); + listFilesArgs.put(FILE_EXPECTED_TO_BE_PRESENT, fileNameClonedUser); + listFilesArgs.put(FILE_NOT_EXPECTED_TO_BE_PRESENT, fileName); + runDeviceTestAsUserInPkgA("testGetFilesInDirectoryViaMediaProviderRespectsUserId", + Integer.parseInt(sCloneUserId), listFilesArgs); + } + + protected void runDeviceTestAsUserInPkgA(@Nonnull String testMethod, int userId, + @Nonnull Map<String, String> args) throws Exception { + DeviceTestRunOptions deviceTestRunOptions = + new DeviceTestRunOptions(DEVICE_TEST_APP_PACKAGE) + .setDevice(getDevice()) + .setTestClassName(DEVICE_TEST_CLASS) + .setTestMethodName(testMethod) + .setMaxInstrumentationTimeoutMs(DEFAULT_INSTRUMENTATION_TIMEOUT_MS) + .setUserId(userId); + for (Map.Entry<String, String> entry : args.entrySet()) { + deviceTestRunOptions.addInstrumentationArg(entry.getKey(), entry.getValue()); + } + assertWithMessage(testMethod + " failed").that( + runDeviceTests(deviceTestRunOptions)).isTrue(); + } +} diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java index 5587a88465d..6bc732860b5 100644 --- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java +++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/BaseHostTestCase.java @@ -16,6 +16,8 @@ package android.scopedstorage.cts.host; +import static com.google.common.truth.Truth.assertThat; + import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.device.NativeDevice; @@ -28,6 +30,11 @@ import com.android.tradefed.util.CommandStatus; abstract class BaseHostTestCase extends BaseHostJUnit4Test { private int mCurrentUserId = NativeDevice.INVALID_USER_ID; private static final String ERROR_MESSAGE_TAG = "[ERROR]"; + protected static ITestDevice sDevice = null; + + protected static void setDevice(ITestDevice device) { + sDevice = device; + } protected String executeShellCommand(String cmd, Object... args) throws Exception { return getDevice().executeShellCommand(String.format(cmd, args)); @@ -42,14 +49,14 @@ abstract class BaseHostTestCase extends BaseHostJUnit4Test { } // TODO (b/174775905) remove after exposing the check from ITestDevice. - protected boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException { - String result = getDevice() + protected static boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException { + String result = sDevice .executeShellCommand("getprop ro.fw.mu.headless_system_user").trim(); return "true".equalsIgnoreCase(result); } - protected boolean isAtLeastS() throws DeviceNotAvailableException { - return getDevice().getApiLevel() >= 31 /* BUILD.VERSION_CODES.S */; + protected static boolean isAtLeastS() throws DeviceNotAvailableException { + return sDevice.getApiLevel() >= 31 /* BUILD.VERSION_CODES.S */; } protected static void eventually(ThrowingRunnable r, long timeoutMillis) { @@ -79,7 +86,7 @@ abstract class BaseHostTestCase extends BaseHostJUnit4Test { return mCurrentUserId; } - protected boolean isSuccessful(CommandResult result) { + protected static boolean isSuccessful(CommandResult result) { if (!CommandStatus.SUCCESS.equals(result.getStatus())) { return false; } @@ -91,6 +98,22 @@ abstract class BaseHostTestCase extends BaseHostJUnit4Test { return (stderr == null || stderr.trim().isEmpty()); } + protected static boolean supportsMultipleUsers() throws DeviceNotAvailableException { + return sDevice.getMaxNumberOfUsersSupported() > 1; + } + + protected static boolean usesSdcardFs() throws Exception { + CommandResult out = sDevice.executeShellV2Command("cat /proc/mounts"); + assertThat(isSuccessful(out)).isTrue(); + for (String line : out.getStdout().split("\n")) { + String[] split = line.split(" "); + if (split.length >= 3 && split[2].equals("sdcardfs")) { + return true; + } + } + return false; + } + private void setCurrentUserId() throws Exception { if (mCurrentUserId != NativeDevice.INVALID_USER_ID) return; diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java index c7887f82040..7590679b3aa 100644 --- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java +++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java @@ -26,6 +26,7 @@ import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -310,6 +311,7 @@ public class ScopedStorageHostTest extends BaseHostTestCase { } @Test + @Ignore("b/247099819") public void testClearPackageData() throws Exception { grantPermissions("android.permission.READ_EXTERNAL_STORAGE"); try { diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/AppCloningDeviceTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/AppCloningDeviceTest.java new file mode 100644 index 00000000000..6df4c8fb68b --- /dev/null +++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/AppCloningDeviceTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 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.scopedstorage.cts; + +import static android.scopedstorage.cts.lib.TestUtils.canOpenFileAs; +import static android.scopedstorage.cts.lib.TestUtils.createFileAs; +import static android.scopedstorage.cts.lib.TestUtils.listAs; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Bundle; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.cts.install.lib.TestApp; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +@RunWith(AndroidJUnit4.class) +public class AppCloningDeviceTest { + + private static final String EMPTY_STRING = ""; + private static final String EXTERNAL_STORAGE_DCIM_PATH = "/storage/emulated/%d/DCIM"; + + // An app with no permissions + private static final TestApp APP_B_NO_PERMS = new TestApp("TestAppB", + "android.scopedstorage.cts.testapp.B.noperms", 1, false, + "CtsScopedStorageTestAppB.apk"); + + @Test + public void testInsertFilesInDirectoryViaMediaProvider() throws Exception { + String dirPath = String.format(EXTERNAL_STORAGE_DCIM_PATH, + Integer.parseInt(getCurrentUserId())); + final File dir = new File(dirPath); + assertThat(dir.exists()).isTrue(); + final File file = new File(dir, getFileToBeCreatedName()); + assertThat(createFileAs(APP_B_NO_PERMS, file.getPath())).isTrue(); + assertThat(canOpenFileAs(APP_B_NO_PERMS, file, true)).isTrue(); + assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).contains(file.getName()); + } + + @Test + public void testGetFilesInDirectoryViaMediaProviderRespectsUserId() throws Exception { + String dirPath = String.format(EXTERNAL_STORAGE_DCIM_PATH, + Integer.parseInt(getCurrentUserId())); + final File dir = new File(dirPath); + assertThat(dir.exists()).isTrue(); + final File expectedFile = new File(dir, getFileToBeExpectedName()); + assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).contains(expectedFile.getName()); + final File notExpectedFile = new File(dir, getFileNotToBeExpectedName()); + assertThat(listAs(APP_B_NO_PERMS, dir.getPath())).doesNotContain(notExpectedFile.getName()); + } + + private String getTestArgumentValueForGivenKey(String testArgumentKey) { + final Bundle testArguments = InstrumentationRegistry.getArguments(); + String testArgumentValue = testArguments.getString(testArgumentKey, EMPTY_STRING); + return testArgumentValue; + } + + private String getCurrentUserId() { + return getTestArgumentValueForGivenKey("currentUserId"); + } + + private String getFileToBeCreatedName() { + return getTestArgumentValueForGivenKey("fileToBeCreated"); + } + + private String getFileToBeExpectedName() { + return getTestArgumentValueForGivenKey("fileExpectedToBePresent"); + } + + private String getFileNotToBeExpectedName() { + return getTestArgumentValueForGivenKey("fileNotExpectedToBePresent"); + } +} diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java index 1bb701bcba0..253a7b88e07 100644 --- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java +++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java @@ -1151,22 +1151,24 @@ public class ScopedStorageTest { executeShellCommand("pm clear --user " + getCurrentUser() + " " + testAppPackageName); - // Wait a max of 5 seconds for the cleaning after "pm clear" command to complete. + // Wait a max of 10 seconds for the cleaning after "pm clear" command to complete. int i = 0; - while(i < 10 && getFileRowIdFromDatabase(fileToBeDeleted) != -1 + while (i < 20 && getFileRowIdFromDatabase(fileToBeDeleted) != -1 && getFileRowIdFromDatabase(nestedFileToBeDeleted) != -1) { Thread.sleep(500); i++; } - - assertThat(getFileOwnerPackageFromDatabase(fileToRemain)).isNull(); - assertThat(getFileRowIdFromDatabase(fileToRemain)).isNotEqualTo(-1); - - assertThat(getFileOwnerPackageFromDatabase(fileToBeDeleted)).isNull(); assertThat(getFileRowIdFromDatabase(fileToBeDeleted)).isEqualTo(-1); - - assertThat(getFileOwnerPackageFromDatabase(nestedFileToBeDeleted)).isNull(); assertThat(getFileRowIdFromDatabase(nestedFileToBeDeleted)).isEqualTo(-1); + + // Poll for package name to be cleared for existing files + i = 0; + while (i < 20 && getFileOwnerPackageFromDatabase(fileToRemain) != null) { + Thread.sleep(500); + i++; + } + assertThat(getFileRowIdFromDatabase(fileToRemain)).isNotEqualTo(-1); + assertThat(getFileOwnerPackageFromDatabase(fileToRemain)).isNull(); } finally { deleteFilesAs(APP_B_NO_PERMS, fileToRemain); deleteFilesAs(APP_B_NO_PERMS, fileToBeDeleted); diff --git a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java index 52141c668ef..bea2bd82ce4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java @@ -47,7 +47,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Scanner; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -258,48 +257,6 @@ public class AdbUtils { } /** - * Enables malloc debug on a given process. - * - * @param processName the name of the process to run with libc malloc debug - * @param device the device to use - * @return true if enabling malloc debug succeeded - */ - public static boolean enableLibcMallocDebug(String processName, ITestDevice device) throws Exception { - device.executeShellCommand("setprop libc.debug.malloc.program " + processName); - device.executeShellCommand("setprop libc.debug.malloc.options \"backtrace guard\""); - /** - * The pidof command is being avoided because it does not exist on versions before M, and - * it behaves differently between M and N. - * Also considered was the ps -AoPID,CMDLINE command, but ps does not support options on - * versions before O. - * The [^]] prefix is being used for the grep command to avoid the case where the output of - * ps includes the grep command itself. - */ - String cmdOut = device.executeShellCommand("ps -A | grep '[^]]" + processName + "'"); - /** - * .hasNextInt() checks if the next token can be parsed as an integer, not if any remaining - * token is an integer. - * Example command: $ ps | fgrep mediaserver - * Out: media 269 1 77016 24416 binder_thr 00f35142ec S /system/bin/mediaserver - * The second field of the output is the PID, which is needed to restart the process. - */ - Scanner s = new Scanner(cmdOut).useDelimiter("\\D+"); - if(!s.hasNextInt()) { - CLog.w("Could not find pid for process: " + processName); - return false; - } - - String result = device.executeShellCommand("kill -9 " + s.nextInt()); - if(!result.equals("")) { - CLog.w("Could not restart process: " + processName); - return false; - } - - TimeUnit.SECONDS.sleep(1); - return true; - } - - /** * Pushes and installs an apk to the selected device * * @param pathToApk a string path to apk from the /res folder diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java index 1f5caa52834..b461fae5608 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java @@ -22,14 +22,14 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Bug_182810085 extends StsExtraBusinessLogicHostTestBase { +public class Bug_182810085 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_182810085"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-182810085.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java index 75bbd0ac298..f0b6568d57e 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java @@ -23,10 +23,10 @@ import org.junit.Test; import org.junit.Before; import org.junit.runner.RunWith; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183613671 extends StsExtraBusinessLogicHostTestBase { +public final class Bug_183613671 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_183613671"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183613671.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java index adf6103043a..7b183b387d9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java @@ -25,10 +25,10 @@ import org.junit.Before; import org.junit.runner.RunWith; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183963253 extends StsExtraBusinessLogicHostTestBase { +public final class Bug_183963253 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_183963253"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183963253.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java index 3aa0474a422..32a1e6ce404 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java @@ -23,13 +23,13 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0015 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2020_0015 extends NonRootSecurityTestCase { @AppModeFull @AsbSecurityTest(cveBugId = 139017101) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java index 2bc254e0724..094eaea3d9c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java @@ -19,7 +19,7 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0338 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2020_0338 extends NonRootSecurityTestCase { @AppModeFull @AsbSecurityTest(cveBugId = 123700107) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java index 27e202cf759..63c812854d2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0448 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2020_0448 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2020-0448.apk"; static final String TEST_PKG = "android.security.cts.CVE_2020_0448"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java index 4b1bc22e33f..9df42ae7cf7 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java @@ -22,7 +22,7 @@ import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.After; import org.junit.Assert; @@ -38,7 +38,7 @@ import org.junit.runner.RunWith; * collected from the hostside and reported accordingly. */ @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0305 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0305 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0305"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0305.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java index b828d2dcba8..1476e911236 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0315 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0315 extends NonRootSecurityTestCase { static final String TEST_PKG = "android.security.cts.CVE_2021_0315"; @After diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java index b3b0f90dce1..57b9a86c191 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0441 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0441 extends NonRootSecurityTestCase { static final String TEST_PKG = "android.security.cts.CVE_2021_0441"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; static final String TEST_APP = "CVE-2021-0441.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java new file mode 100644 index 00000000000..45a1905b145 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 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.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0487 extends NonRootSecurityTestCase { + private static final String TEST_PKG = "android.security.cts.CVE_2021_0487"; + + /** + * b/174046397 + * Vulnerable app : CalendarProvider.apk + * Vulnerable module : com.android.providers.calendar + * Is Play managed : No + */ + @AsbSecurityTest(cveBugId = 174046397) + @Test + public void testPocCVE_2021_0487() { + ITestDevice device = null; + try { + device = getDevice(); + /* Wake up the screen */ + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + /* Install the application */ + installPackage("CVE-2021-0487.apk"); + + AdbUtils.runCommandLine( + "pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", device); + runDeviceTests(TEST_PKG, TEST_PKG + "." + "DeviceTest", "testOverlayButtonPresence"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // return to home screen + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + } catch (Exception e) { + // ignore exceptions here + } + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java index 30af4725490..7cd63605906 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java @@ -21,7 +21,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0523 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0523 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.cve_2021_0523"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0523.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java index 5a7ec8d1c24..f775822dfb6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java @@ -20,14 +20,14 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0586 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0586 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.cve_2021_0586"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0586.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java index eb74b201862..92c64351de4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java @@ -21,7 +21,7 @@ import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.RequiresDevice; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import java.util.regex.Pattern; import org.junit.Assert; import org.junit.Before; @@ -33,7 +33,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeTrue; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0591 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0591 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0591"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java index 29fd2b39bf2..2e1ddda5360 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java @@ -19,7 +19,7 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0642 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0642 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2021-0642.apk"; static final String TEST_PKG = "android.security.cts.cve_2021_0642"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java index 26bba4a6d50..15c59efc97e 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java @@ -19,14 +19,14 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0685 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0685 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.cve_2021_0685"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0685.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java index bf261fd0eab..01a3c07160b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java @@ -22,7 +22,7 @@ import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.log.LogUtil.CLog; import org.junit.After; @@ -38,7 +38,7 @@ import static org.hamcrest.CoreMatchers.*; * Test installs sample app and then tries to overwrite *.apk file */ @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0691 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0691 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0691"; private static final String TEST_APP = "CVE-2021-0691.apk"; private static final String DEVICE_TMP_DIR = "/data/local/tmp/"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java index 2b7ad1452d2..98deb18e315 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java @@ -19,13 +19,13 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0693 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0693 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0693"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java index fabaf89437a..9225b561ece 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java @@ -20,13 +20,13 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0706 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0706 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0706"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java index 760c265fe09..d4f6a4517e2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java @@ -20,7 +20,7 @@ import android.platform.test.annotations.AppModeFull; import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.log.LogUtil.CLog; import org.junit.After; import org.junit.Assert; @@ -30,7 +30,7 @@ import org.junit.runner.RunWith; import static org.junit.Assert.*; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0921 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0921 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0921"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0921.apk"; @@ -57,7 +57,7 @@ public class CVE_2021_0921 extends StsExtraBusinessLogicHostTestBase { getDevice().executeShellCommand("input keyevent KEYCODE_MENU"); //run the test - Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "test")); + runDeviceTests(TEST_PKG, TEST_CLASS, "test"); CLog.i("testRunDeviceTest() end"); } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java index cbf108883a1..d83f26a9529 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java @@ -23,7 +23,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; @@ -31,7 +31,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0928 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0928 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0928"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0928.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java index ecb6bdd3cd4..833b93aa854 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java @@ -19,13 +19,13 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0953 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0953 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 184046278) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java index 95c90d4ec4b..847feefa489 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0954 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0954 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0954"; @AsbSecurityTest(cveBugId = 143559931) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java index 65934f2741f..b7b0e2bdabc 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java @@ -21,7 +21,7 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0965 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0965 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0965"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0965.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java index 3b12ce5a926..c47ebf119b6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39626 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39626 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2021-39626.apk"; static final String TEST_PKG = "android.security.cts.CVE_2021_39626"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java index 444f1a55a60..f75514208c6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java @@ -23,13 +23,13 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39692 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39692 extends NonRootSecurityTestCase { @AppModeFull @AsbSecurityTest(cveBugId = 209611539) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java index acc6a2ed00f..63235ecf78b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java @@ -23,7 +23,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,7 +31,7 @@ import org.junit.runner.RunWith; import java.io.File; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39700 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39700 extends NonRootSecurityTestCase { /** * b/201645790 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java index f8d6fe6f1d5..5e78a90cd85 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39701 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39701 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 212286849) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java index cf8a688976b..cf5d47c3dd8 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java @@ -21,14 +21,14 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39702 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39702 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_39702"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-39702.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java index af72d3b81de..9aebd152144 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java @@ -22,13 +22,13 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39704 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39704 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 209965481) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java index cd8afef86e4..ecf096f31d1 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java @@ -21,7 +21,7 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39706 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39706 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 200164168) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java index 2e61b7064a2..e40cea6bbfb 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java @@ -21,7 +21,7 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39707 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39707 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 200688991) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java index 0ae1efa8e83..d67b4e6d53f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39794 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39794 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2021-39794-test.apk"; static final String RECEIVER_APP = "CVE-2021-39794-receiver.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java index 4755ddbb229..a427e65169a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39795 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39795 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_39795"; private static final String DIR_PATH = "/storage/emulated/0/Android/data/CVE-2021-39795-dir"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java index f90cae0c295..07fa92757d9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java @@ -20,14 +20,14 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39796 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39796 extends NonRootSecurityTestCase { static final int USER_ID = 0; static final String TEST_PKG = "android.security.cts.CVE_2021_39796"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java index ee835f50c93..1707ce92429 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java @@ -20,13 +20,13 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39797 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39797 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 209607104) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java index a55229abe45..f1eaad2edf7 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39808 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39808 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 209966086) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java index f9520824b26..9745336f8aa 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java @@ -21,14 +21,14 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39810 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39810 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 212610736) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java index df8701c9e67..ec4d1977c01 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20004 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20004 extends NonRootSecurityTestCase { final static String TEST_PKG = "android.security.cts.CVE_2022_20004_test"; final static String PROVIDER_PKG = "android.security.cts.CVE_2022_20004_provider"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java index 55e6dca25fc..abc94f5837c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20007 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20007 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 211481342) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java index a8256d6bef0..e83f090e94d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20115 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20115 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2022_20115"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2022-20115.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java index 45c6fb137fc..9e5e7eb94b4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20138 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20138 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2022-20138.apk"; static final String TEST_PKG = "android.security.cts.CVE_2022_20138"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java index ebfed1a4523..3d31cee17c7 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20197 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20197 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2022_20197"; @AsbSecurityTest(cveBugId = 208279300) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java index f593f204bd6..18d4cdd6f55 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java @@ -21,7 +21,7 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20223 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20223 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 223578534) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java index 1886a4af4ac..59e7631b287 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20230 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20230 extends NonRootSecurityTestCase { public static final int USER_ID = 0; static final String TEST_APP = "CVE-2022-20230.apk"; static final String TEST_PKG = "android.security.cts.CVE_2022_20230"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java index de245bb06da..8087e692b5f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20347 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20347 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 228450811) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java index 0f66dfdd87c..df33a31144d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20348 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20348 extends NonRootSecurityTestCase { static final String TEST_PKG = "android.security.cts.CVE_2022_20348"; public static final String TEST_DEVICE_ADMIN_RECEIVER = ".PocDeviceAdminReceiver"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java index c4f52254d66..f8dcc48de61 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20349 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20349 extends NonRootSecurityTestCase { static final String TEST_PKG = "android.security.cts.CVE_2022_20349"; public static final String TEST_DEVICE_ADMIN_RECEIVER = ".PocDeviceAdminReceiver"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java index e661b4ff4a1..12bb187b47f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20353 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20353 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 221041256) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20501.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20501.java new file mode 100644 index 00000000000..067a011aa88 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20501.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 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.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2022_20501 extends NonRootSecurityTestCase { + private ITestDevice mDevice; + + /** + * b/246933359 + * Vulnerable app : Telecom.apk + * Vulnerable module : com.android.server.telecom + * Is Play managed : No + */ + @AsbSecurityTest(cveBugId = 246933359) + @Test + public void testPocCVE_2022_20501() { + try { + final String testPkg = "android.security.cts.CVE_2022_20501"; + mDevice = getDevice(); + installPackage("CVE-2022-20501.apk"); + + // Wake up the device + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP && wm dismiss-keyguard", + mDevice); + + AdbUtils.runCommandLine( + "pm grant " + testPkg + " android.permission.SYSTEM_ALERT_WINDOW", mDevice); + runDeviceTests(testPkg, testPkg + ".DeviceTest", "testOverlayButtonPresence"); + } catch (Exception e) { + assumeNoException(e); + } + } + + @After + public void tearDown() { + try { + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", mDevice); + } catch (Exception ignored) { + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp new file mode 100644 index 00000000000..14d6baf8331 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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: "CVE-2021-0487", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml new file mode 100644 index 00000000000..5c5934c342b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 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.security.cts.CVE_2021_0487"> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <application> + <service android:name=".PocService" /> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_0487" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml new file mode 100644 index 00000000000..de5f2533e30 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 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> + <integer name="assumptionFailure">-1</integer> + <integer name="pass">0</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml new file mode 100644 index 00000000000..4d4098d003a --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 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="broadcastReceiverCalendar">CalendarProviderBroadcastReceiver</string> + <string name="calendarClsName">%1$s.CalendarDebugActivity</string> + <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string> + <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string> + <string name="dumpsysActivity">dumpsys activity %1$s</string> + <string name="errorMessage">Device is vulnerable to b/174046397 hence any app with + "SYSTEM_ALERT_WINDOW" can overlay the CalendarDebugActivity screen</string> + <string name="messageKey">message</string> + <string name="overlayButtonText">OverlayButton</string> + <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string> + <string name="resultKey">result</string> + <string name="resumedTrue">mResumed=true</string> + <string name="sharedPreferences">CVE_2021_0487_prefs</string> + <string name="vulActivityNotRunningError">The CalendarDebugActivity is not currently + running on the device</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java new file mode 100644 index 00000000000..0a3e65db579 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2021_0487; + +import static android.provider.CalendarContract.ACTION_EVENT_REMINDER; +import static android.provider.CalendarContract.CONTENT_URI; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Resources; +import android.net.Uri; +import android.provider.Settings; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private static final int LAUNCH_TIMEOUT_MS = 10000; + private String mVulnerablePkgName = ""; + private Context mContext = getApplicationContext(); + Resources mResources; + + private void startOverlayService() { + Intent intent = new Intent(mContext, PocService.class); + assumeTrue(mContext.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(mContext)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startService(intent); + } + + private void startVulnerableActivity() { + Intent intent = new Intent(ACTION_EVENT_REMINDER); + Intent vulnPkgNameIntent = new Intent(); + intent.setData(CONTENT_URI); + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> ris = pm.queryBroadcastReceivers(intent, 0); + for (ResolveInfo ri : ris) { + if (ri.activityInfo.name + .contains(mContext.getString(R.string.broadcastReceiverCalendar))) { + mVulnerablePkgName = ri.activityInfo.packageName; + } + } + vulnPkgNameIntent.setClassName(mVulnerablePkgName, + mContext.getString(R.string.calendarClsName, mVulnerablePkgName)); + vulnPkgNameIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(vulnPkgNameIntent); + } + + @Test + public void testOverlayButtonPresence() { + try { + UiDevice device = UiDevice.getInstance(getInstrumentation()); + + /* Go to home screen */ + device.pressHome(); + + /* Start the overlay service */ + startOverlayService(); + mResources = mContext.getResources(); + + SharedPreferences sharedPrefs = mContext.getSharedPreferences( + mResources.getString(R.string.sharedPreferences), Context.MODE_APPEND); + Semaphore preferenceChanged = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(mResources.getString(R.string.resultKey))) { + preferenceChanged.release(); + } + } + }; + sharedPrefs.registerOnSharedPreferenceChangeListener(listener); + + /* Wait for the overlay service to set some result in shared preferences */ + assumeTrue(preferenceChanged.tryAcquire(LAUNCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + int result = sharedPrefs.getInt(mResources.getString(R.string.resultKey), + mResources.getInteger(R.integer.assumptionFailure)); + String message = sharedPrefs.getString(mResources.getString(R.string.messageKey), + mResources.getString(R.string.defaultSemaphoreMsg)); + assumeTrue(message, result != mResources.getInteger(R.integer.assumptionFailure)); + + /* Wait for the overlay window */ + Pattern overlayTextPattern = Pattern.compile( + mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE); + assumeTrue(mContext.getString(R.string.overlayUiScreenError), + device.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS)); + + /* Start the vulnerable activity */ + startVulnerableActivity(); + + /* Wait until the object of overlay window is gone */ + boolean overlayDisallowed = + device.wait(Until.gone(By.pkg(mContext.getPackageName())), LAUNCH_TIMEOUT_MS); + + /* Check if the currently running activity is the vulnerable activity */ + String activityDump = device.executeShellCommand( + mContext.getString(R.string.dumpsysActivity, mVulnerablePkgName)); + Pattern activityPattern = Pattern.compile(mContext.getString(R.string.resumedTrue), + Pattern.CASE_INSENSITIVE); + assumeTrue(mContext.getString(R.string.vulActivityNotRunningError), + activityPattern.matcher(activityDump).find()); + + /* Failing the test as fix is not present */ + assertTrue(mContext.getString(R.string.errorMessage), overlayDisallowed); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java new file mode 100644 index 00000000000..1a8cd0f2a3a --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2021_0487; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.IBinder; +import android.view.Gravity; +import android.view.WindowManager; +import android.widget.Button; + +import androidx.annotation.IntegerRes; + +public class PocService extends Service { + private Button mButton; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mLayoutParams; + + int getInteger(@IntegerRes int resId) { + return getResources().getInteger(resId); + } + + private static int getScreenWidth() { + return Resources.getSystem().getDisplayMetrics().widthPixels; + } + + private static int getScreenHeight() { + return Resources.getSystem().getDisplayMetrics().heightPixels; + } + + @Override + public void onCreate() { + super.onCreate(); + try { + mWindowManager = getSystemService(WindowManager.class); + mLayoutParams = new WindowManager.LayoutParams(); + mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLayoutParams.format = PixelFormat.OPAQUE; + mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + mLayoutParams.width = getScreenWidth(); + mLayoutParams.height = getScreenHeight(); + mLayoutParams.x = getScreenWidth() / 2; + mLayoutParams.y = getScreenHeight() / 2; + } catch (Exception e) { + sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage()); + } + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + try { + showFloatingWindow(); + sendTestResult(getInteger(R.integer.pass), ""); + } catch (Exception e) { + sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage()); + } + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onDestroy() { + try { + mWindowManager.removeView(mButton); + super.onDestroy(); + } catch (Exception e) { + // ignore the exception + } + } + + private void showFloatingWindow() { + mButton = new Button(this); + mButton.setText(getString(R.string.overlayButtonText)); + mWindowManager.addView(mButton, mLayoutParams); + mButton.setTag(mButton.getVisibility()); + } + + private void sendTestResult(int result, String message) { + try { + SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), + Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), result); + edit.putString(getString(R.string.messageKey), message); + edit.commit(); + } catch (Exception e) { + // ignore the exception + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java index 167a849c948..3b1948552a4 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java @@ -11,8 +11,6 @@ import android.util.Log; import java.io.File; -import static org.junit.Assume.assumeNoException; - public class Trigger { private static final String TAG = "TAG_2021_0921.Trigger"; private Context mContext; @@ -26,8 +24,19 @@ public class Trigger { return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); } + private static boolean isWear(Context context) { + PackageManager pm = context.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_WATCH); + } + + private static boolean isTv(Context context) { + PackageManager pm = context.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION); + } + public void accountSettings() { Log.d(TAG, "accountSettings() start"); + String exitMessage = "accountSettings() end"; //replaces intent.setAction(Intent.ACTION_REBOOT) in original Poc Intent arbitraryIntent = new Intent(mContext, TestActivity.class); @@ -36,8 +45,19 @@ public class Trigger { Authenticator.mIntent = arbitraryIntent; Intent intent = new Intent(); - String pkg = isCar(mContext) ? "com.android.car.settings" : "com.android.settings"; - intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountSettings")); + String pkg = "com.android.settings"; + if (isCar(mContext)) { + pkg = "com.android.car.settings"; + intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountActivity")); + } else if (isTv(mContext)) { + pkg = "com.android.tv.settings"; + intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountWithTypeActivity")); + } else if (isWear(mContext)) { + pkg = "com.google.android.clockwork.home"; + intent.setComponent(new ComponentName(pkg, pkg + ".accounts.SetupAccountReceiver")); + } else { + intent.setComponent(new ComponentName(pkg, pkg + ".accounts.AddAccountSettings")); + } intent.setAction(Intent.ACTION_RUN); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); String authTypes[] = {"android.security.cts"}; @@ -47,8 +67,9 @@ public class Trigger { try { mContext.startActivity(intent); } catch (ActivityNotFoundException e) { - assumeNoException(e); + // activity does not exist on this device + exitMessage = "accountSettings() failure: activity does not exist on this device"; } - Log.d(TAG, "accountSettings() end"); + Log.d(TAG, exitMessage); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/Android.bp new file mode 100644 index 00000000000..94988810449 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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: "CVE-2022-20501", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/AndroidManifest.xml new file mode 100644 index 00000000000..5789b655741 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 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.security.cts.CVE_2022_20501"> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <application> + <service android:name=".PocService" + android:enabled="true" /> + </application> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2022_20501" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/res/values/strings.xml new file mode 100644 index 00000000000..2af8c0e3338 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/res/values/strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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="activityNotFoundMsg">The activity with intent %1$s was not found</string> + <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string> + <string name="cmdDumpsysActivity">dumpsys activity %1$s</string> + <string name="dumpsysActivityException">Could not execute dumpsys activity command</string> + <string name="overlayButtonText">CVE_2022_20501_OverlayButton</string> + <string name="overlayErrorMessage">Device is vulnerable to b/246933359 hence any app with + "SYSTEM_ALERT_WINDOW permission can overlay the %1$s screen</string> + <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string> + <string name="mResumedTrue">mResumed=true</string> + <string name="telUri">tel:6505551212</string> + <string name="telecomPkgDefault">com.android.server.telecom</string> + <string name="vulClsName">.settings.EnableAccountPreferenceActivity</string> + <string name="vulActivityNotRunningError">The %1$s is not currently running on the device + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/DeviceTest.java new file mode 100644 index 00000000000..d6f51982d62 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/DeviceTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20501; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; + +import android.app.UiAutomation; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private Context mContext; + + private void startOverlayService() { + Intent intent = new Intent(mContext, PocService.class); + assumeTrue(mContext.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(mContext)); + mContext.startService(intent); + } + + private String getTelecomPkgName() { + PackageManager pm = getInstrumentation().getTargetContext().getPackageManager(); + UiAutomation ui = getInstrumentation().getUiAutomation(); + String name = mContext.getString(R.string.telecomPkgDefault); + try { + ui.adoptShellPermissionIdentity(android.Manifest.permission.INTERACT_ACROSS_USERS); + Intent intent = new Intent(Intent.ACTION_CALL); + intent.setData(Uri.parse(mContext.getString(R.string.telUri))); + ResolveInfo info = pm.resolveActivityAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY, + UserHandle.USER_SYSTEM); + name = info.activityInfo.packageName; + } catch (Exception e) { + assumeNoException(e); + } finally { + ui.dropShellPermissionIdentity(); + } + return name; + } + + @Test + public void testOverlayButtonPresence() { + try { + mContext = getApplicationContext(); + UiDevice device = UiDevice.getInstance(getInstrumentation()); + + // Start the overlay service + startOverlayService(); + + // Wait for the overlay window + Pattern overlayTextPattern = Pattern.compile( + mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE); + final long launchTimeoutMs = 20_000L; + assumeTrue(mContext.getString(R.string.overlayUiScreenError), + device.wait(Until.hasObject(By.text(overlayTextPattern)), launchTimeoutMs)); + + // Start the vulnerable activity + String pkg = getTelecomPkgName(); + String cls = mContext.getString(R.string.vulClsName); + Intent intent = new Intent(); + String vulActivity = pkg + cls; + intent.setClassName(pkg, vulActivity); + PackageManager pm = mContext.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + assumeNotNull(mContext.getString(R.string.activityNotFoundMsg, intent), ri); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + + // Wait until overlay window is gone + boolean overlayDisallowed = + device.wait(Until.gone(By.text(overlayTextPattern)), launchTimeoutMs); + + // Check if the currently running activity is the vulnerable activity + String activityDump = device.executeShellCommand( + mContext.getString(R.string.cmdDumpsysActivity, vulActivity)); + + Pattern activityPattern = Pattern.compile(mContext.getString(R.string.mResumedTrue), + Pattern.CASE_INSENSITIVE); + assumeTrue(mContext.getString(R.string.vulActivityNotRunningError, vulActivity), + activityPattern.matcher(activityDump).find()); + + // Failing the test as overlay window is being allowed making code vulnerable + assertTrue(mContext.getString(R.string.overlayErrorMessage, vulActivity), + overlayDisallowed); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/PocService.java new file mode 100644 index 00000000000..8e1c5d380ca --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20501/src/android/security/cts/CVE_2022_20501/PocService.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20501; + +import android.app.Service; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.IBinder; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.widget.Button; + +public class PocService extends Service { + private Button mButton; + private WindowManager mWindowManager; + + @Override + public void onCreate() { + try { + super.onCreate(); + mWindowManager = getSystemService(WindowManager.class); + LayoutParams layoutParams = new LayoutParams(); + layoutParams.type = LayoutParams.TYPE_APPLICATION_OVERLAY; + layoutParams.flags = + LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; + layoutParams.format = PixelFormat.OPAQUE; + layoutParams.gravity = Gravity.LEFT | Gravity.TOP; + DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics(); + layoutParams.width = displayMetrics.widthPixels; + layoutParams.height = displayMetrics.heightPixels; + layoutParams.x = displayMetrics.widthPixels / 2; + layoutParams.y = displayMetrics.heightPixels / 2; + + // Show the floating window + mButton = new Button(this); + mButton.setText(getString(R.string.overlayButtonText)); + mWindowManager.addView(mButton, layoutParams); + } catch (Exception ignored) { + // In case of occurrence of an exception overlay won't appear on display which results in + // assumption failure in device test. Hence ignoring this exception here. + } + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onDestroy() { + try { + mWindowManager.removeView(mButton); + } catch (Exception ignored) { + } + super.onDestroy(); + } +} diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java index 42b9b5c9cd7..781be6b575f 100644 --- a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java +++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java @@ -34,7 +34,6 @@ import com.android.os.AtomsProto.AppBreadcrumbReported; import com.android.os.AtomsProto.AppCrashOccurred; import com.android.os.AtomsProto.AppUsageEventOccurred; import com.android.os.AtomsProto.Atom; -import com.android.os.AtomsProto.AttributionNode; import com.android.os.AtomsProto.AudioStateChanged; import com.android.os.AtomsProto.CameraStateChanged; import com.android.os.AtomsProto.FlashlightStateChanged; @@ -49,6 +48,7 @@ import com.android.os.AtomsProto.TestAtomReported; import com.android.os.AtomsProto.UiEventReported; import com.android.os.AtomsProto.VibratorStateChanged; import com.android.os.AtomsProto.WakelockStateChanged; +import com.android.os.AttributionNode; import com.android.os.StatsLog.EventMetricData; import com.android.tradefed.build.IBuildInfo; import com.android.tradefed.log.LogUtil; diff --git a/tests/PhotoPicker/AndroidManifest.xml b/tests/PhotoPicker/AndroidManifest.xml index e10bf5219ad..ca87690e795 100644 --- a/tests/PhotoPicker/AndroidManifest.xml +++ b/tests/PhotoPicker/AndroidManifest.xml @@ -19,7 +19,21 @@ package="android.photopicker.cts"> <application android:label="Photo Picker Device Tests"> <uses-library android:name="android.test.runner" /> - <activity android:name="android.photopicker.cts.GetResultActivity" /> + <activity android:name="android.photopicker.cts.GetResultActivity" + android:exported="false" + android:enabled="true"> + <intent-filter android:priority="999"> + <action android:name="android.provider.action.PICK_IMAGES" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="image/*" /> + <data android:mimeType="video/*" /> + </intent-filter> + <intent-filter android:priority="999"> + <action android:name="android.provider.action.PICK_IMAGES" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + <provider android:name="android.photopicker.cts.cloudproviders.CloudProviderPrimary" android:authorities="android.photopicker.cts.cloudproviders.cloud_primary" diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java index c7824bb2520..1746f4ecd14 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java @@ -18,13 +18,12 @@ package android.photopicker.cts; import static android.photopicker.cts.util.GetContentActivityAliasUtils.clearPackageData; import static android.photopicker.cts.util.GetContentActivityAliasUtils.getDocumentsUiPackageName; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertReadOnlyAccess; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUriAndPath; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAndClickBrowse; - +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertReadOnlyAccess; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -33,7 +32,7 @@ import android.content.ClipData; import android.content.Intent; import android.net.Uri; import android.photopicker.cts.util.GetContentActivityAliasUtils; -import android.photopicker.cts.util.PhotoPickerUiUtils; +import android.photopicker.cts.util.UiAssertionUtils; import android.util.Pair; import androidx.test.uiautomator.UiObject; @@ -42,9 +41,7 @@ import androidx.test.uiautomator.UiScrollable; import androidx.test.uiautomator.UiSelector; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import java.util.ArrayList; @@ -73,26 +70,19 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { if (mActivity != null) { mActivity.finish(); } - } - @BeforeClass - public static void setUpBeforeClass() throws Exception { - sDocumentsUiPackageName = getDocumentsUiPackageName(); - sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState(); + GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState); } @Before public void setUp() throws Exception { super.setUp(); + sDocumentsUiPackageName = getDocumentsUiPackageName(); + sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState(); clearPackageData(sDocumentsUiPackageName); } - @AfterClass - public static void tearDownAfterClass() throws Exception { - GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState); - } - @Test public void testMimeTypeFilter() throws Exception { final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); @@ -100,7 +90,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("audio/*"); mActivity.startActivityForResult(intent, REQUEST_CODE); - mDevice.waitForIdle(); + sDevice.waitForIdle(); // Should open documentsUi assertThatShowsDocumentsUiButtons(); @@ -116,7 +106,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { intent.setType("image/*"); intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"video/*", "audio/*"}); mActivity.startActivityForResult(intent, REQUEST_CODE); - mDevice.waitForIdle(); + sDevice.waitForIdle(); // Should open documentsUi assertThatShowsDocumentsUiButtons(); @@ -140,7 +130,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { intent.setType("image/*"); mActivity.startActivityForResult(intent, REQUEST_CODE); - findAndClickBrowse(mDevice); + findAndClickBrowse(sDevice); findAndClickFilesInDocumentsUi(fileNameList); @@ -166,7 +156,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { intent.setType("image/*"); mActivity.startActivityForResult(intent, REQUEST_CODE); - findAndClickBrowse(mDevice); + findAndClickBrowse(sDevice); findAndClickFilesInDocumentsUi(fileNameList); @@ -185,7 +175,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { mActivity.startActivityForResult(Intent.createChooser(intent, TAG), REQUEST_CODE); // Should open Picker - assertThatShowsPickerUi(); + UiAssertionUtils.assertThatShowsPickerUi(); } @Test @@ -207,7 +197,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { findAndClickMediaIcon(); // Should open Picker - assertThatShowsPickerUi(); + UiAssertionUtils.assertThatShowsPickerUi(); } private void findAndClickMediaIcon() throws Exception { @@ -219,30 +209,13 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { new UiObject(appList).waitForExists(SHORT_TIMEOUT)).isTrue(); String photoPickerAppName = "Media"; - UiObject mediaButton = mDevice.findObject(new UiSelector().text(photoPickerAppName)); + UiObject mediaButton = sDevice.findObject(new UiSelector().text(photoPickerAppName)); assertWithMessage("Timed out waiting for " + photoPickerAppName + " app icon to appear") .that(new UiScrollable(appList).scrollIntoView(mediaButton)).isTrue(); - mDevice.waitForIdle(); - - clickAndWait(mDevice, mediaButton); - } - - private void assertThatShowsPickerUi() { - // Assert that Search bar for DocumentsUi shows - // Add a short timeout wait for DocumentsUi to show - assertThat(new UiObject(new UiSelector().resourceIdMatches( - PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/bottom_sheet")) - .waitForExists(SHORT_TIMEOUT)).isTrue(); - - // Assert that "Recent files" header for DocumentsUi shows - assertThat(new UiObject(new UiSelector().resourceIdMatches( - PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/privacy_text")) - .exists()).isTrue(); + sDevice.waitForIdle(); - // Assert that Documents list UiObject for DocumentsUi shows - assertThat(new UiObject(new UiSelector().text("Photos")).exists()).isTrue(); - assertThat(new UiObject(new UiSelector().text("Albums")).exists()).isTrue(); + clickAndWait(sDevice, mediaButton); } private void assertThatShowsDocumentsUiButtons() { @@ -268,7 +241,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { private void findAndClickSelect() throws Exception { final UiObject selectButton = new UiObject(new UiSelector().resourceId( sDocumentsUiPackageName + ":id/action_menu_select")); - clickAndWait(mDevice, selectButton); + clickAndWait(sDevice, selectButton); } private void findAndClickFileInDocumentsUi(String fileName) throws Exception { @@ -284,7 +257,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { // Enforce to set the list mode // Because UiScrollable can't reach the real bottom (when WEB_LINKABLE_FILE item) // in grid mode when screen landscape mode - clickAndWait(mDevice, new UiObject(new UiSelector().resourceId(sDocumentsUiPackageName + clickAndWait(sDevice, new UiObject(new UiSelector().resourceId(sDocumentsUiPackageName + ":id/sub_menu_list"))); } catch (UiObjectNotFoundException ignored) { // Do nothing, already be in list mode. @@ -306,9 +279,9 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { } } - mDevice.swipe(/* startX= */ mDevice.getDisplayWidth() / 2, - /* startY= */ mDevice.getDisplayHeight() / 2, - /* endX= */ mDevice.getDisplayWidth() / 2, + sDevice.swipe(/* startX= */ sDevice.getDisplayWidth() / 2, + /* startY= */ sDevice.getDisplayHeight() / 2, + /* endX= */ sDevice.getDisplayWidth() / 2, /* endY= */ 0, /* steps= */ 40); } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java index 0f61dc74bbc..6cd0a7b8eed 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java @@ -16,15 +16,15 @@ package android.photopicker.cts; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPersistedGrant; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPickerUriFormat; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -36,7 +36,9 @@ import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.Intent; import android.net.Uri; +import android.photopicker.cts.util.UiAssertionUtils; import android.provider.MediaStore; +import android.util.Log; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiObject; @@ -55,6 +57,7 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class ActionPickImagesOnlyTest extends PhotoPickerBaseTest { + private static final String TAG = "ActionPickImagesOnlyTest"; private List<Uri> mUriList = new ArrayList<>(); @After @@ -70,6 +73,24 @@ public class ActionPickImagesOnlyTest extends PhotoPickerBaseTest { } @Test + public void testPhotoPickerIntentDelegation() throws Exception { + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + + for (String mimeType: new String[] { + null, + "image/*", + "video/*" + }) { + Log.d(TAG, "Testing Photo Picker intent delegation with MimeType " + mimeType); + intent.setType(mimeType); + + mActivity.startActivityForResult(Intent.createChooser(intent, TAG), REQUEST_CODE); + + UiAssertionUtils.assertThatShowsPickerUi(); + } + } + + @Test public void testMultiSelect_invalidParam() throws Exception { final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit() + 1); @@ -102,10 +123,10 @@ public class ActionPickImagesOnlyTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(imageCount); // Select maxCount + 1 item for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - UiObject snackbarTextView = mDevice.findObject(new UiSelector().text( + UiObject snackbarTextView = sDevice.findObject(new UiSelector().text( "Select up to 2 items")); assertWithMessage("Timed out while waiting for snackbar to appear").that( snackbarTextView.waitForExists(SHORT_TIMEOUT)).isTrue(); @@ -113,7 +134,7 @@ public class ActionPickImagesOnlyTest extends PhotoPickerBaseTest { assertWithMessage("Timed out waiting for snackbar to disappear").that( snackbarTextView.waitUntilGone(SHORT_TIMEOUT)).isTrue(); - clickAndWait(mDevice, findAddButton()); + clickAndWait(sDevice, findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -132,7 +153,7 @@ public class ActionPickImagesOnlyTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); // Select 1 item - clickAndWait(mDevice, itemList.get(0)); + clickAndWait(sDevice, itemList.get(0)); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); diff --git a/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java index ceaf3b244e9..c89716f8d27 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java @@ -20,11 +20,11 @@ import static android.os.SystemProperties.getBoolean; import static android.photopicker.cts.PickerProviderMediaGenerator.MediaGenerator; import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvider; import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess; import static android.provider.MediaStore.PickerMediaColumns; import static com.google.common.truth.Truth.assertThat; @@ -374,12 +374,12 @@ public class CloudPhotoPickerTest extends PhotoPickerBaseTest { for (int i = 0; i < itemList.size(); i++) { final UiObject item = itemList.get(i); item.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); } final UiObject addButton = findAddButton(); addButton.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); return mActivity.getResult().data.getClipData(); } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java index 79d4b2bebb5..ca5a251cbb8 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java @@ -16,17 +16,13 @@ package android.photopicker.cts; -import android.Manifest; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; -import android.provider.DeviceConfig; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; -import com.android.modules.utils.build.SdkLevel; - import org.junit.Before; /** @@ -35,32 +31,31 @@ import org.junit.Before; */ public class PhotoPickerBaseTest { public static int REQUEST_CODE = 42; + private static final Instrumentation sInstrumentation = + InstrumentationRegistry.getInstrumentation(); + protected static final UiDevice sDevice = UiDevice.getInstance(sInstrumentation); protected GetResultActivity mActivity; protected Context mContext; - protected UiDevice mDevice; @Before public void setUp() throws Exception { - final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); - mDevice = UiDevice.getInstance(inst); - final String setSyncDelayCommand = "device_config put storage pickerdb.default_sync_delay_ms 0"; - mDevice.executeShellCommand(setSyncDelayCommand); + sDevice.executeShellCommand(setSyncDelayCommand); - mContext = inst.getContext(); + mContext = sInstrumentation.getContext(); final Intent intent = new Intent(mContext, GetResultActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Wake up the device and dismiss the keyguard before the test starts - mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); - mDevice.executeShellCommand("wm dismiss-keyguard"); + sDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); + sDevice.executeShellCommand("wm dismiss-keyguard"); - mActivity = (GetResultActivity) inst.startActivitySync(intent); + mActivity = (GetResultActivity) sInstrumentation.startActivitySync(intent); // Wait for the UI Thread to become idle. - inst.waitForIdleSync(); + sInstrumentation.waitForIdleSync(); mActivity.clearResult(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java index 48c1ea16b54..e06873ea867 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java @@ -16,14 +16,14 @@ package android.photopicker.cts; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; import static android.photopicker.cts.util.PhotoPickerUiUtils.findProfileButton; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPickerUriFormat; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -90,7 +90,7 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { // Click the profile button to change to personal profile final UiObject profileButton = findProfileButton(); profileButton.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); final List<UiObject> itemList = findItemList(imageCount); final int itemCount = itemList.size(); @@ -98,12 +98,12 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { for (int i = 0; i < itemCount; i++) { final UiObject item = itemList.get(i); item.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); } final UiObject addButton = findAddButton(); addButton.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -144,7 +144,7 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { // Click the profile button to change to work profile final UiObject profileButton = findProfileButton(); profileButton.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); assertBlockedByAdminDialog(isInvokedFromWorkProfile); } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java new file mode 100644 index 00000000000..bfa0462dc18 --- /dev/null +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerSettingsTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 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.photopicker.cts; + +import static android.os.SystemProperties.getBoolean; +import static android.photopicker.cts.util.PhotoPickerUiUtils.verifyActionBarExists; +import static android.photopicker.cts.util.PhotoPickerUiUtils.verifySettingsActivityIsVisible; + +import android.content.Intent; +import android.photopicker.cts.util.PhotoPickerUiUtils; +import android.provider.MediaStore; + +import androidx.test.uiautomator.UiObject; + +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Photo Picker tests for settings page launched from the overflow menu in PhotoPickerActivity or + * the Settings app. + */ +public class PhotoPickerSettingsTest extends PhotoPickerBaseTest { + + private static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot"; + private static final String ALLOWED_CLOUD_PROVIDERS_KEY = "allowed_cloud_providers"; + private static final String SETTINGS_SYS_PROP = + "debug.photopicker.enable_settings_screen"; + + private static String sPreviouslyAllowedCloudProviders; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + // Store current allowed cloud providers for reset at the end of tests. + sPreviouslyAllowedCloudProviders = sDevice.executeShellCommand( + String.format("device_config get %s %s", NAMESPACE_STORAGE_NATIVE_BOOT, + ALLOWED_CLOUD_PROVIDERS_KEY)); + + // Enable Settings menu item in PhotoPickerActivity's overflow menu. + sDevice.executeShellCommand( + String.format("device_config put %s %s not_empty", NAMESPACE_STORAGE_NATIVE_BOOT, + ALLOWED_CLOUD_PROVIDERS_KEY)); + sDevice.executeShellCommand(String.format("setprop %s true", SETTINGS_SYS_PROP)); + Assume.assumeTrue(getBoolean(SETTINGS_SYS_PROP, false)); + } + + @AfterClass + public static void tearDownClass() throws Exception { + // Reset allowed cloud providers device config. + if (sPreviouslyAllowedCloudProviders == null + || sPreviouslyAllowedCloudProviders.isBlank()) { + // Delete the device config since `device_config put` does not support empty values. + sDevice.executeShellCommand( + String.format("device_config delete %s %s", NAMESPACE_STORAGE_NATIVE_BOOT, + ALLOWED_CLOUD_PROVIDERS_KEY)); + } else { + sDevice.executeShellCommand( + String.format("device_config put %s %s %s", NAMESPACE_STORAGE_NATIVE_BOOT, + ALLOWED_CLOUD_PROVIDERS_KEY, sPreviouslyAllowedCloudProviders)); + } + + // Disable Settings menu item in PhotoPickerActivity's overflow menu. + sDevice.executeShellCommand(String.format("setprop %s false", SETTINGS_SYS_PROP)); + Assume.assumeFalse(getBoolean(SETTINGS_SYS_PROP, true)); + } + + @Test + public void testSettingsLaunchFromOverflowMenu() throws Exception { + // Launch PhotoPickerActivity. + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + // Click on the Settings menu item in the overflow menu. + final UiObject settingsMenuItem = PhotoPickerUiUtils.findSettingsOverflowMenuItem(sDevice); + PhotoPickerUiUtils.clickAndWait(sDevice, settingsMenuItem); + + // Verify PhotoPickerSettingsActivity is launched and visible. + verifySettingsActivityIsVisible(sDevice); + verifyActionBarExists(sDevice); + } +} diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java index 440e47288c2..11d99d1ed41 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java @@ -18,11 +18,6 @@ package android.photopicker.cts; import static android.photopicker.cts.util.GetContentActivityAliasUtils.clearPackageData; import static android.photopicker.cts.util.GetContentActivityAliasUtils.getDocumentsUiPackageName; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertContainsMimeType; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertMimeType; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat; -import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createDNGVideosAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createVideosAndGetUris; @@ -34,6 +29,11 @@ import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddOrSelectButton; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertContainsMimeType; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertMimeType; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPersistedGrant; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertPickerUriFormat; +import static android.photopicker.cts.util.ResultsAssertionsUtils.assertRedactedReadOnlyAccess; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -52,8 +52,7 @@ import androidx.test.uiautomator.UiObjectNotFoundException; import androidx.test.uiautomator.UiSelector; import org.junit.After; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -83,17 +82,14 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { private static int sGetContentTakeOverActivityAliasState; - @BeforeClass - public static void setUpBeforeClass() throws Exception { + @Before + public void setUp() throws Exception { + super.setUp(); + sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState(); clearPackageData(getDocumentsUiPackageName()); } - @AfterClass - public static void tearDownAfterClass() throws Exception { - GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState); - } - @After public void tearDown() throws Exception { for (Uri uri : mUriList) { @@ -104,6 +100,8 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { if (mActivity != null) { mActivity.finish(); } + + GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState); } @Test @@ -115,7 +113,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { launchPhotoPickerForIntent(intent); final UiObject item = findItemList(itemCount).get(0); - clickAndWait(mDevice, item); + clickAndWait(sDevice, item); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); @@ -132,14 +130,14 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final Intent intent = new Intent(mAction); launchPhotoPickerForIntent(intent); - UiObject albumsTab = mDevice.findObject(new UiSelector().text( + UiObject albumsTab = sDevice.findObject(new UiSelector().text( "Albums")); - clickAndWait(mDevice, albumsTab); + clickAndWait(sDevice, albumsTab); final UiObject album = findItemList(1).get(0); - clickAndWait(mDevice, album); + clickAndWait(sDevice, album); final UiObject item = findItemList(itemCount).get(0); - clickAndWait(mDevice, item); + clickAndWait(sDevice, item); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); @@ -156,11 +154,11 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { addMultipleSelectionFlag(intent); launchPhotoPickerForIntent(intent); - UiObject albumsTab = mDevice.findObject(new UiSelector().text( + UiObject albumsTab = sDevice.findObject(new UiSelector().text( "Albums")); - clickAndWait(mDevice, albumsTab); + clickAndWait(sDevice, albumsTab); final UiObject album = findItemList(1).get(0); - clickAndWait(mDevice, album); + clickAndWait(sDevice, album); final List<UiObject> itemList = findItemList(videoCount); final int itemCount = itemList.size(); @@ -168,10 +166,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(videoCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - clickAndWait(mDevice, findViewSelectedButton()); + clickAndWait(sDevice, findViewSelectedButton()); // Wait for playback to start. This is needed in some devices where playback // buffering -> ready state takes around 10s. @@ -189,11 +187,11 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final UiObject item = findItemList(itemCount).get(0); item.longClick(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); final UiObject addButton = findPreviewAddOrSelectButton(); assertThat(addButton.waitForExists(1000)).isTrue(); - clickAndWait(mDevice, addButton); + clickAndWait(sDevice, addButton); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); @@ -212,10 +210,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - clickAndWait(mDevice, findAddButton()); + clickAndWait(sDevice, findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -243,26 +241,26 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(videoCount); // Select one item from Photo grid - clickAndWait(mDevice, itemList.get(0)); + clickAndWait(sDevice, itemList.get(0)); // Preview the item UiObject item = itemList.get(1); item.longClick(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); final UiObject addOrSelectButton = findPreviewAddOrSelectButton(); assertWithMessage("Timed out waiting for AddOrSelectButton to appear") .that(addOrSelectButton.waitForExists(1000)).isTrue(); // Select the item from Preview - clickAndWait(mDevice, addOrSelectButton); + clickAndWait(sDevice, addOrSelectButton); - mDevice.pressBack(); + sDevice.pressBack(); // Select one more item from Photo grid - clickAndWait(mDevice, itemList.get(2)); + clickAndWait(sDevice, itemList.get(2)); - clickAndWait(mDevice, findAddButton()); + clickAndWait(sDevice, findAddButton()); // Verify that all 3 items are returned final ClipData clipData = mActivity.getResult().data.getClipData(); @@ -289,10 +287,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - clickAndWait(mDevice, findViewSelectedButton()); + clickAndWait(sDevice, findViewSelectedButton()); // Swipe left three times swipeLeftAndWait(); @@ -300,10 +298,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { swipeLeftAndWait(); // Deselect one item - clickAndWait(mDevice, findPreviewSelectedCheckButton()); + clickAndWait(sDevice, findPreviewSelectedCheckButton()); // Return selected items - clickAndWait(mDevice, findPreviewAddButton()); + clickAndWait(sDevice, findPreviewAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -350,20 +348,20 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Test 2: Click Mute Button // Click to unmute the audio - clickAndWait(mDevice, muteButton); + clickAndWait(sDevice, muteButton); waitForBinderCallsToComplete(); // Check that mute button state is unmute, i.e., it shows `volume up` icon assertMuteButtonState(muteButton, /* isMuted */ false); // Click on the muteButton and check that mute button status is now 'mute' - clickAndWait(mDevice, muteButton); + clickAndWait(sDevice, muteButton); waitForBinderCallsToComplete(); assertMuteButtonState(muteButton, /* isMuted */ true); // Click on the muteButton and check that mute button status is now unmute - clickAndWait(mDevice, muteButton); + clickAndWait(sDevice, muteButton); waitForBinderCallsToComplete(); @@ -371,8 +369,8 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Test 3: Next preview resumes mute state // Go back and launch preview again - mDevice.pressBack(); - clickAndWait(mDevice, findViewSelectedButton()); + sDevice.pressBack(); + clickAndWait(sDevice, findViewSelectedButton()); waitForBinderCallsToComplete(); @@ -408,7 +406,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Test 2: Swipe resumes mute state, with state of mute button 'volume up' / 'unmute' // Click muteButton again to check the next video resumes the previous video's mute state - clickAndWait(mDevice, muteButton); + clickAndWait(sDevice, muteButton); waitForBinderCallsToComplete(); @@ -462,12 +460,12 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final UiObject muteButton = findMuteButton(); // unmute the audio of video preview - clickAndWait(mDevice, muteButton); + clickAndWait(sDevice, muteButton); // Remote video preview involves binder calls // Wait for Binder calls to complete and device to be idle MediaStore.waitForIdle(mContext.getContentResolver()); - mDevice.waitForIdle(); + sDevice.waitForIdle(); assertMuteButtonState(muteButton, /* isMuted */ false); @@ -512,7 +510,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final UiObject playerView = findPlayerView(); // Click on StyledPlayerView to make the video controls visible - clickAndWait(mDevice, playerView); + clickAndWait(sDevice, playerView); assertPlayerControlsVisible(playPauseButton, muteButton); // Wait for 1s and check that controls are still visible @@ -521,7 +519,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Click on StyledPlayerView and check that controls are no longer visible. Don't click in // the center, clicking in the center may pause the video. playerView.clickBottomRight(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); assertPlayerControlsHidden(playPauseButton, muteButton); // Swipe left and check that controls are not visible @@ -529,7 +527,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertPlayerControlsHidden(playPauseButton, muteButton); // Click on the StyledPlayerView and check that controls appear - clickAndWait(mDevice, playerView); + clickAndWait(sDevice, playerView); assertPlayerControlsVisible(playPauseButton, muteButton); // Swipe left to check that controls are now visible on swipe @@ -562,10 +560,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isAtLeast(videoCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - clickAndWait(mDevice, findAddButton()); + clickAndWait(sDevice, findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -608,10 +606,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isAtLeast(totalCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - clickAndWait(mDevice, findAddButton()); + clickAndWait(sDevice, findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); assertWithMessage("Expected number of items returned to be: " + itemCount) @@ -645,10 +643,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isAtLeast(videoCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - clickAndWait(mDevice, findAddButton()); + clickAndWait(sDevice, findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); assertWithMessage("Expected number of items returned to be: " + itemCount) @@ -681,16 +679,16 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertPlayerControlsAutoHide(playPauseButton, muteButton); // Click on StyledPlayerView to make the video controls visible - clickAndWait(mDevice, findPlayerView()); + clickAndWait(sDevice, findPlayerView()); // PlayPause button is now pause button, click the button to pause the video. - clickAndWait(mDevice, playPauseButton); + clickAndWait(sDevice, playPauseButton); // Wait for 1s and check that play button is not auto hidden assertPlayerControlsDontAutoHide(playPauseButton, muteButton); // PlayPause button is now play button, click the button to play the video. - clickAndWait(mDevice, playPauseButton); + clickAndWait(sDevice, playPauseButton); // Check that pause button auto-hides in 1s. assertPlayerControlsAutoHide(playPauseButton, muteButton); } @@ -709,10 +707,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(videoCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(mDevice, itemList.get(i)); + clickAndWait(sDevice, itemList.get(i)); } - clickAndWait(mDevice, findViewSelectedButton()); + clickAndWait(sDevice, findViewSelectedButton()); // Wait for playback to start. This is needed in some devices where playback // buffering -> ready state takes around 10s. @@ -725,7 +723,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { private void waitForBinderCallsToComplete() { // Wait for Binder calls to complete and device to be idle MediaStore.waitForIdle(mContext.getContentResolver()); - mDevice.waitForIdle(); + sDevice.waitForIdle(); } private void setUpAndAssertStickyPlayerControls(UiObject playerView, UiObject playPauseButton, @@ -735,7 +733,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Wait for 1s or Play/Pause button to hide playPauseButton.waitUntilGone(1000); // Click on StyledPlayerView to make the video controls visible - clickAndWait(mDevice, playerView); + clickAndWait(sDevice, playerView); assertPlayerControlsVisible(playPauseButton, muteButton); } @@ -811,10 +809,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { } private void swipeLeftAndWait() { - final int width = mDevice.getDisplayWidth(); - final int height = mDevice.getDisplayHeight(); - mDevice.swipe(15 * width / 20, height / 2, width / 20, height / 2, 10); - mDevice.waitForIdle(); + final int width = sDevice.getDisplayWidth(); + final int height = sDevice.getDisplayHeight(); + sDevice.swipe(15 * width / 20, height / 2, width / 20, height / 2, 10); + sDevice.waitForIdle(); } private static List<String> getTestParameters() { diff --git a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java index db60d7ddd8c..20b22c56acf 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java @@ -92,7 +92,7 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { Assume.assumeTrue(getBoolean("sys.photopicker.pickerdb.enabled", true)); - mDevice.executeShellCommand("setprop sys.photopicker.remote_preview true"); + sDevice.executeShellCommand("setprop sys.photopicker.remote_preview true"); Assume.assumeTrue(getBoolean("sys.photopicker.remote_preview", true)); mCloudPrimaryMediaGenerator = PickerProviderMediaGenerator.getMediaGenerator( @@ -134,7 +134,7 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { // TODO(b/215187981): Add test for onMediaPause() // Exit preview mode - mDevice.pressBack(); + sDevice.pressBack(); // Remote Preview calls onSurfaceDestroyed, check if the id is the same (as the // CloudMediaProvider is only rendering to one surface id) @@ -354,7 +354,7 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { + "retriable error") .that(findPreviewErrorAlertDialogRetryButton().waitForExists(SHORT_TIMEOUT)) .isTrue(); - clickAndWait(mDevice, findPreviewErrorAlertDialogRetryButton()); + clickAndWait(sDevice, findPreviewErrorAlertDialogRetryButton()); mAssertInOrder.verify(mSurfaceControllerListener).onMediaPlay(eq(surfaceId)); } @@ -402,12 +402,12 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { for (final UiObject item : itemList) { item.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); } final UiObject viewSelectedButton = findViewSelectedButton(); viewSelectedButton.click(); - mDevice.waitForIdle(); + sDevice.waitForIdle(); // Wait for CloudMediaProvider binder calls to finish. MediaStore.waitForIdle(mContext.getContentResolver()); @@ -419,20 +419,20 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { } private void swipeLeftAndWait() throws Exception { - final int width = mDevice.getDisplayWidth(); - final int height = mDevice.getDisplayHeight(); - mDevice.swipe(width / 2, height / 2, width / 4, height / 2, 10); - mDevice.waitForIdle(); + final int width = sDevice.getDisplayWidth(); + final int height = sDevice.getDisplayHeight(); + sDevice.swipe(width / 2, height / 2, width / 4, height / 2, 10); + sDevice.waitForIdle(); // Wait for CloudMediaProvider binder calls to finish. MediaStore.waitForIdle(mContext.getContentResolver()); } private void swipeRightAndWait() throws Exception { - final int width = mDevice.getDisplayWidth(); - final int height = mDevice.getDisplayHeight(); - mDevice.swipe(width / 4, height / 2, width / 2, height / 2, 10); - mDevice.waitForIdle(); + final int width = sDevice.getDisplayWidth(); + final int height = sDevice.getDisplayHeight(); + sDevice.swipe(width / 4, height / 2, width / 2, height / 2, 10); + sDevice.waitForIdle(); // Wait for CloudMediaProvider binder calls to finish. MediaStore.waitForIdle(mContext.getContentResolver()); diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java index 8f58f3e3261..5cdb5927ea3 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java @@ -95,18 +95,49 @@ public class PhotoPickerUiUtils { } public static void findAndClickBrowse(UiDevice uiDevice) throws Exception { - assertWithMessage("Timed out waiting for overflow menu to appear") - .that(new UiObject(new UiSelector().description("More options")) - .waitForExists(SHORT_TIMEOUT)) - .isTrue(); - - final UiObject overflowMenu = new UiObject(new UiSelector().description("More options")); + final UiObject overflowMenu = getOverflowMenuObject(uiDevice); clickAndWait(uiDevice, overflowMenu); final UiObject browseButton = new UiObject(new UiSelector().textContains("Browse")); clickAndWait(uiDevice, browseButton); } + public static UiObject findSettingsOverflowMenuItem(UiDevice uiDevice) throws Exception { + final UiObject overflowMenu = getOverflowMenuObject(uiDevice); + clickAndWait(uiDevice, overflowMenu); + return new UiObject(new UiSelector().textContains("Settings")); + } + + public static UiObject getOverflowMenuObject(UiDevice uiDevice) { + // Wait for overflow menu to appear. + verifyOverflowMenuExists(uiDevice); + return new UiObject(new UiSelector().description("More options")); + } + + public static void verifyActionBarExists(UiDevice uiDevice) { + assertWithMessage("Timed out waiting for action bar to appear") + .that(new UiObject(new UiSelector() + .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/action_bar")) + .waitForExists(TIMEOUT)) + .isTrue(); + } + + private static void verifyOverflowMenuExists(UiDevice uiDevice) { + assertWithMessage("Timed out waiting for overflow menu to appear") + .that(new UiObject(new UiSelector().description("More options")) + .waitForExists(TIMEOUT)) + .isTrue(); + } + + public static void verifySettingsActivityIsVisible(UiDevice uiDevice) { + // id/settings_activity_root is the root layout in activity_photo_picker_settings.xml + assertWithMessage("Timed out waiting for settings activity to appear") + .that(new UiObject(new UiSelector() + .resourceIdMatches(REGEX_PACKAGE_NAME + ":id/settings_activity_root")) + .waitForExists(TIMEOUT)) + .isTrue(); + } + public static void clickAndWait(UiDevice uiDevice, UiObject uiObject) throws Exception { uiObject.click(); uiDevice.waitForIdle(); diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/ResultsAssertionsUtils.java index 6d86cee66b9..e08dd2a1e2e 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/ResultsAssertionsUtils.java @@ -46,9 +46,9 @@ import java.util.Arrays; import java.util.List; /** - * Photo Picker Utility methods for test assertions. + * Photo Picker Utility methods for PhotoPicker result assertions. */ -public class PhotoPickerAssertionsUtils { +public class ResultsAssertionsUtils { private static final String TAG = "PhotoPickerTestAssertions"; public static void assertPickerUriFormat(Uri uri, int expectedUserId) { diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/UiAssertionUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/UiAssertionUtils.java new file mode 100644 index 00000000000..7caeb6ad833 --- /dev/null +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/UiAssertionUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 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.photopicker.cts.util; + +import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiSelector; + +/** + * Photo Picker Utility methods for PhotoPicker UI assertions. + */ +public class UiAssertionUtils { + /** + * Verifies PhotoPicker UI is shown. + */ + public static void assertThatShowsPickerUi() { + // Assert that Bottom Sheet is shown + // Add a short timeout wait for PhotoPicker to show + assertThat(new UiObject(new UiSelector().resourceIdMatches( + PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/bottom_sheet")) + .waitForExists(SHORT_TIMEOUT)).isTrue(); + + // Assert that privacy text is shown + assertThat(new UiObject(new UiSelector().resourceIdMatches( + PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/privacy_text")) + .exists()).isTrue(); + + // Assert that "Photos" and "Albums" headers are shown. + assertThat(new UiObject(new UiSelector().text("Photos")).exists()).isTrue(); + assertThat(new UiObject(new UiSelector().text("Albums")).exists()).isTrue(); + } +} diff --git a/tests/accessibility/Android.bp b/tests/accessibility/Android.bp index 91ce26b6898..03c7d1b85a7 100644 --- a/tests/accessibility/Android.bp +++ b/tests/accessibility/Android.bp @@ -21,6 +21,7 @@ java_library_static { sdk_version: "test_current", static_libs: [ "compatibility-device-util-axt", + "sts-device-util", ], srcs: ["common/src/**/*.java"], } @@ -39,6 +40,7 @@ android_test { test_suites: [ "cts", "general-tests", + "sts", ], sdk_version: "test_current", } diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml index bf3b1a82567..056ca59ec8a 100644 --- a/tests/accessibility/AndroidManifest.xml +++ b/tests/accessibility/AndroidManifest.xml @@ -60,6 +60,17 @@ android:resource="@xml/speaking_and_vibrating_accessibilityservice"/> </service> + <service android:name=".NoFeedbackAccessibilityService" + android:label="@string/title_no_feedback_accessibility_service" + android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" + android:exported="true"> + <intent-filter> + <action android:name="android.accessibilityservice.AccessibilityService"/> + </intent-filter> + <meta-data android:name="android.accessibilityservice" + android:resource="@xml/no_feedback_accessibilityservice"/> + </service> + <service android:name=".AccessibilityButtonService" android:label="@string/title_accessibility_button_service" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" diff --git a/tests/accessibility/res/values/strings.xml b/tests/accessibility/res/values/strings.xml index 37d30516bc0..871d5f9955a 100644 --- a/tests/accessibility/res/values/strings.xml +++ b/tests/accessibility/res/values/strings.xml @@ -26,6 +26,9 @@ <!-- String title for the vibrating accessibility service --> <string name="title_speaking_and_vibrating_accessibility_service">Speaking and Vibrating Accessibility Service</string> + <!-- String title for the no-feedback accessibility service --> + <string name="title_no_feedback_accessibility_service">No-Feedback Accessibility Service</string> + <!-- String title for the accessibility button service --> <string name="title_accessibility_button_service">Accessibility Button Service</string> diff --git a/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml b/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml new file mode 100644 index 00000000000..168e5845031 --- /dev/null +++ b/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 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"/> diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java index 0f5afd15ef6..27c3aac53cf 100644 --- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java +++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java @@ -34,6 +34,7 @@ import android.app.UiAutomation; import android.content.Context; import android.content.pm.ServiceInfo; import android.os.Handler; +import android.platform.test.annotations.AsbSecurityTest; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; @@ -44,6 +45,8 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + import com.android.compatibility.common.util.PollingCheck; import com.android.compatibility.common.util.SettingsStateChangerRule; import com.android.compatibility.common.util.SystemUtil; @@ -63,7 +66,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * Class for testing {@link AccessibilityManager}. */ @RunWith(AndroidJUnit4.class) -public class AccessibilityManagerTest { +public class AccessibilityManagerTest extends StsExtraBusinessLogicTestCase { private AccessibilityDumpOnFailureRule mDumpOnFailureRule = new AccessibilityDumpOnFailureRule(); @@ -81,6 +84,11 @@ public class AccessibilityManagerTest { new InstrumentedAccessibilityServiceTestRule<>( SpeakingAndVibratingAccessibilityService.class, false); + private InstrumentedAccessibilityServiceTestRule<NoFeedbackAccessibilityService> + mNoFeedbackAccessibilityServiceRule = + new InstrumentedAccessibilityServiceTestRule<>( + NoFeedbackAccessibilityService.class, false); + private static final Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation(); @@ -93,6 +101,9 @@ public class AccessibilityManagerTest { private static final String MULTIPLE_FEEDBACK_TYPES_ACCESSIBILITY_SERVICE_NAME = "android.view.accessibility.cts.SpeakingAndVibratingAccessibilityService"; + private static final String NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME = + "android.view.accessibility.cts.NoFeedbackAccessibilityService"; + public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS = "accessibility_non_interactive_ui_timeout_ms"; @@ -112,6 +123,7 @@ public class AccessibilityManagerTest { // SettingsStateChangerRule will suppress accessibility services, so it should be // executed before enabling a11y services and after disabling a11y services. .outerRule(mAudioDescriptionSetterRule) + .around(mNoFeedbackAccessibilityServiceRule) .around(mSpeakingAndVibratingAccessibilityServiceRule) .around(mVibratingAccessibilityServiceRule) .around(mSpeakingAccessibilityServiceRule) @@ -241,6 +253,26 @@ public class AccessibilityManagerTest { assertTrue("The vibrating service should be enabled.", vibratingServiceEnabled); } + @AsbSecurityTest(cveBugId = {243849844}) + @Test + public void testGetEnabledAccessibilityServiceList_NoFeedback() { + mNoFeedbackAccessibilityServiceRule.enableService(); + List<AccessibilityServiceInfo> enabledServices = + mAccessibilityManager.getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + boolean noFeedbackServiceEnabled = false; + final int serviceCount = enabledServices.size(); + for (int i = 0; i < serviceCount; i++) { + AccessibilityServiceInfo enabledService = enabledServices.get(i); + ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; + if (mTargetContext.getPackageName().equals(serviceInfo.packageName) + && NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME.equals(serviceInfo.name)) { + noFeedbackServiceEnabled = true; + } + } + assertTrue("The no-feedback service should be enabled.", noFeedbackServiceEnabled); + } + @Test public void testGetEnabledAccessibilityServiceListForType() throws Exception { mSpeakingAccessibilityServiceRule.enableService(); diff --git a/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java new file mode 100644 index 00000000000..0c79ae41515 --- /dev/null +++ b/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 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.view.accessibility.cts; + +import android.accessibility.cts.common.InstrumentedAccessibilityService; +import android.content.ComponentName; + +/** + * Stub accessibility service that reports itself as providing no feedback. + */ +public class NoFeedbackAccessibilityService extends InstrumentedAccessibilityService { + public static final ComponentName COMPONENT_NAME = new ComponentName( + "android.view.accessibility.cts", + "android.view.accessibility.cts.NoFeedbackAccessibilityService"); +} diff --git a/tests/accessibilityservice/Android.bp b/tests/accessibilityservice/Android.bp index 876da5a62cf..9e891ee86b5 100644 --- a/tests/accessibilityservice/Android.bp +++ b/tests/accessibilityservice/Android.bp @@ -27,6 +27,7 @@ android_test { "platform-test-annotations", "CtsAccessibilityCommon", "CtsInputMethodServiceCommon", + "sts-device-util", ], libs: [ "android.test.runner", @@ -37,10 +38,12 @@ android_test { test_suites: [ "cts", "general-tests", + "sts", ], sdk_version: "test_current", per_testcase_directory: true, data: [ ":CtsInputMethod1", + ":CtsAccessibilityMultipleServicesApp", ":CtsAccessibilityWidgetProvider"], } diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml index 3bde0fd1dd1..ad7994e4fb5 100644 --- a/tests/accessibilityservice/AndroidManifest.xml +++ b/tests/accessibilityservice/AndroidManifest.xml @@ -27,6 +27,8 @@ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> + <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> + <application android:theme="@android:style/Theme.Holo.NoActionBar" android:requestLegacyExternalStorage="true"> diff --git a/tests/accessibilityservice/AndroidTest.xml b/tests/accessibilityservice/AndroidTest.xml index 48aea2c3a0f..8096d296893 100644 --- a/tests/accessibilityservice/AndroidTest.xml +++ b/tests/accessibilityservice/AndroidTest.xml @@ -24,6 +24,11 @@ <option name="run-command" value="cmd accessibility set-bind-instant-service-allowed true" /> <option name="teardown-command" value="cmd accessibility set-bind-instant-service-allowed false" /> </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push-file" key="CtsAccessibilityMultipleServicesApp.apk" + value="/data/local/tmp/cts/content/CtsAccessibilityMultipleServicesApp.apk" /> + </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsAccessibilityServiceTestCases.apk" /> diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java index 5f9733c61c8..52fb4c37e7f 100644 --- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java +++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java @@ -17,7 +17,9 @@ package android.accessibilityservice.cts; import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.accessibility.cts.common.InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE; import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService; +import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType; import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventTypeWithAction; import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventTypeWithResource; @@ -76,7 +78,9 @@ import android.graphics.Region; import android.os.Process; import android.os.SystemClock; import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.Presubmit; +import android.provider.Settings; import android.test.suitebuilder.annotation.MediumTest; import android.text.TextUtils; import android.util.Log; @@ -99,6 +103,9 @@ import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.CtsMouseUtil; +import com.android.compatibility.common.util.ShellUtils; +import com.android.compatibility.common.util.TestUtils; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import org.junit.After; import org.junit.AfterClass; @@ -120,7 +127,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * are generated and their correct dispatch verified. */ @RunWith(AndroidJUnit4.class) -public class AccessibilityEndToEndTest { +public class AccessibilityEndToEndTest extends StsExtraBusinessLogicTestCase { private static final String LOG_TAG = "AccessibilityEndToEndTest"; @@ -1013,6 +1020,71 @@ public class AccessibilityEndToEndTest { } } + @AsbSecurityTest(cveBugId = {243378132}) + @Test + public void testUninstallPackage_DisablesMultipleServices() throws Exception { + final String apkPath = + "/data/local/tmp/cts/content/CtsAccessibilityMultipleServicesApp.apk"; + final String packageName = "foo.bar.multipleservices"; + final ComponentName service1 = ComponentName.createRelative(packageName, ".StubService1"); + final ComponentName service2 = ComponentName.createRelative(packageName, ".StubService2"); + // Match AccessibilityManagerService#COMPONENT_NAME_SEPARATOR + final String componentNameSeparator = ":"; + + final String originalEnabledServicesSetting = getEnabledServicesSetting(); + + try { + // Install the apk in this test method, instead of as part of the target preparer, to + // allow repeated --iterations of the test. + com.google.common.truth.Truth.assertThat( + ShellUtils.runShellCommand("pm install " + apkPath)).startsWith("Success"); + + // Enable the two services and wait until AccessibilityManager reports them as enabled. + final String servicesToEnable = getEnabledServicesSetting() + componentNameSeparator + + service1.flattenToShortString() + componentNameSeparator + + service2.flattenToShortString(); + ShellCommandBuilder.create(sInstrumentation) + .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + servicesToEnable) + .putSecureSetting(Settings.Secure.ACCESSIBILITY_ENABLED, "1") + .run(); + TestUtils.waitUntil("Failed to enable 2 services from package " + packageName, + (int) TIMEOUT_SERVICE_ENABLE / 1000, + () -> getEnabledServices().stream().filter( + info -> info.getId().startsWith(packageName)).count() == 2); + + // Uninstall the package that contains the services. + com.google.common.truth.Truth.assertThat( + ShellUtils.runShellCommand("pm uninstall " + packageName)).startsWith( + "Success"); + + // Ensure the uninstall removed the services from the secure setting. + TestUtils.waitUntil( + "Failed to disable services after uninstalling package " + packageName, + (int) TIMEOUT_SERVICE_ENABLE / 1000, + () -> !getEnabledServicesSetting().contains(packageName)); + } finally { + ShellCommandBuilder.create(sInstrumentation) + .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + originalEnabledServicesSetting) + .run(); + ShellUtils.runShellCommand("pm uninstall " + packageName); + } + } + + private List<AccessibilityServiceInfo> getEnabledServices() { + return ((AccessibilityManager) sInstrumentation.getContext().getSystemService( + Context.ACCESSIBILITY_SERVICE)).getEnabledAccessibilityServiceList( + FEEDBACK_ALL_MASK); + } + + private String getEnabledServicesSetting() { + final String result = Settings.Secure.getString( + sInstrumentation.getContext().getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); + return result != null ? result : ""; + } + private static void assertPackageName(AccessibilityNodeInfo node, String packageName) { if (node == null) { return; diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/Android.bp b/tests/accessibilityservice/test-apps/MultipleServicesApp/Android.bp new file mode 100644 index 00000000000..d8fdbceba09 --- /dev/null +++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/Android.bp @@ -0,0 +1,24 @@ +// Copyright (C) 2022 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: "CtsAccessibilityMultipleServicesApp", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + sdk_version: "test_current", +} diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/AndroidManifest.xml b/tests/accessibilityservice/test-apps/MultipleServicesApp/AndroidManifest.xml new file mode 100644 index 00000000000..649478eda56 --- /dev/null +++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/AndroidManifest.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2022 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="foo.bar.multipleservices" + android:targetSandboxVersion="2"> + <application> + <service android:name="foo.bar.multipleservices.StubService1" + android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" + android:exported="true"> + <intent-filter> + <action android:name="android.accessibilityservice.AccessibilityService"/> + </intent-filter> + <meta-data android:name="android.accessibilityservice" + android:resource="@xml/stub_service"/> + </service> + <service android:name="foo.bar.multipleservices.StubService2" + android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" + android:exported="true"> + <intent-filter> + <action android:name="android.accessibilityservice.AccessibilityService"/> + </intent-filter> + <meta-data android:name="android.accessibilityservice" + android:resource="@xml/stub_service"/> + </service> + </application> +</manifest> diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/res/xml/stub_service.xml b/tests/accessibilityservice/test-apps/MultipleServicesApp/res/xml/stub_service.xml new file mode 100644 index 00000000000..0cbd1399ec9 --- /dev/null +++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/res/xml/stub_service.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 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" /> diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService1.java b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService1.java new file mode 100644 index 00000000000..022f6e19ee8 --- /dev/null +++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService1.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 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 foo.bar.multipleservices; + +import android.accessibilityservice.AccessibilityService; +import android.view.accessibility.AccessibilityEvent; + +/** A stub accessibility service for testing package uninstall. */ +public class StubService1 extends AccessibilityService { + @Override + public void onAccessibilityEvent(AccessibilityEvent event) {} + + @Override + public void onInterrupt() {} +} diff --git a/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService2.java b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService2.java new file mode 100644 index 00000000000..28353c255c0 --- /dev/null +++ b/tests/accessibilityservice/test-apps/MultipleServicesApp/src/foo/bar/multipleservices/StubService2.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 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 foo.bar.multipleservices; + +import android.accessibilityservice.AccessibilityService; +import android.view.accessibility.AccessibilityEvent; + +/** A stub accessibility service for testing package uninstall. */ +public class StubService2 extends AccessibilityService { + @Override + public void onAccessibilityEvent(AccessibilityEvent event) {} + + @Override + public void onInterrupt() {} +} diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml index 0e8466247c9..d4498707eb3 100644 --- a/tests/backup/AndroidTest.xml +++ b/tests/backup/AndroidTest.xml @@ -20,6 +20,7 @@ <!-- Backup of instant apps is not supported. --> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" /> <!-- Run module in system user because backup tests are not fully supported in secondary user. For devices running on secondary user, such as automotive devices, these tests will fail. When backup tests are fully functional for secondary users: diff --git a/tests/backup/TEST_MAPPING b/tests/backup/TEST_MAPPING index 4e5beb081b0..1e1dcdb211c 100644 --- a/tests/backup/TEST_MAPPING +++ b/tests/backup/TEST_MAPPING @@ -3,5 +3,15 @@ { "name": "CtsBackupTestCases" } + ], + "mainline-presubmit": [ + { + "name": "CtsBackupTestCases[com.google.android.permission.apex]", + "options": [ + { + "include-filter": "android.backup.cts.PermissionTest" + } + ] + } ] } diff --git a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java index 2baa2f1d3be..c448a7606a2 100644 --- a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java +++ b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java @@ -16,12 +16,7 @@ package android.location.cts.fine; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; -import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; - -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; @@ -29,8 +24,6 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager.ResolveInfoFlags; import android.location.Geocoder; import android.location.Geocoder.GeocodeListener; import android.platform.test.annotations.AppModeFull; @@ -64,17 +57,6 @@ public class GeocoderTest { mGeocoder = new Geocoder(mContext, Locale.US); } - @Test - public void testIsPresent() { - if (mContext.getPackageManager().queryIntentServices( - new Intent("com.android.location.service.GeocodeProvider"), ResolveInfoFlags.of( - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE)).isEmpty()) { - assertFalse(Geocoder.isPresent()); - } else { - assertTrue(Geocoder.isPresent()); - } - } - @ApiTest(apis = "android.location.Geocoder#getFromLocation") @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps") @Test diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml index 588ddf8dc2a..ef5dbeb6897 100644 --- a/tests/media/AndroidTest.xml +++ b/tests/media/AndroidTest.xml @@ -19,6 +19,14 @@ <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" /> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device --> + <option name="set-test-harness" value="false" /> + <option name="screen-always-on" value="on" /> + <option name="screen-adaptive-brightness" value="off" /> + <option name="disable-audio" value="false"/> + <option name="screen-saver" value="off"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> <option name="target" value="host" /> <option name="config-filename" value="CtsMediaV2TestCases" /> diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java index 9d1d839c47d..61772986c3b 100644 --- a/tests/media/src/android/mediav2/cts/CodecTestBase.java +++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java @@ -594,6 +594,9 @@ abstract class CodecTestBase { public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R); public static final boolean IS_AT_LEAST_T = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU); + //TODO(b/248315681) Remove codenameEquals() check once devices return correct version for U + public static final boolean IS_AT_LEAST_U = ApiLevelUtil.isAfter(Build.VERSION_CODES.TIRAMISU) + || ApiLevelUtil.codenameEquals("UpsideDownCake"); public static final boolean FIRST_SDK_IS_AT_LEAST_T = ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU); public static final boolean VNDK_IS_AT_LEAST_T = diff --git a/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java index 518ff8610a8..9039d38b2cc 100644 --- a/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java +++ b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java @@ -351,7 +351,7 @@ public class DecodeGlAccuracyTest extends CodecDecoderTestBase { // limit the test to devices launching with T assumeTrue("Skipping color range " + mRange + " and color standard " + mStandard + " for devices upgrading to T", - FIRST_SDK_IS_AT_LEAST_T); + FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T); // TODO (b/219748700): Android software codecs work only with 601LR. Skip for now. assumeTrue("Skipping " + mCompName + " for color range " + mRange diff --git a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java index ebed139fc53..6bcd5c77195 100644 --- a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java +++ b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java @@ -27,6 +27,8 @@ import android.view.Surface; import androidx.test.filters.LargeTest; +import com.android.compatibility.common.util.MediaUtils; + import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -138,6 +140,11 @@ public class EncodeDecodeAccuracyTest extends CodecDecoderTestBase { @Before public void setUp() throws IOException { + // Few cuttlefish specific color conversion issues were fixed after Android T. + if (MediaUtils.onCuttlefish()) { + assumeTrue("Color conversion related tests are not valid on cuttlefish releases " + + "through android T", IS_AT_LEAST_U); + } if (mUseHighBitDepth) { assumeTrue("Codec doesn't support ABGR2101010", hasSupportForColorFormat(mCompName, mMime, COLOR_Format32bitABGR2101010)); diff --git a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java index 72e4765a0db..a2563a3e544 100644 --- a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java +++ b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java @@ -29,6 +29,7 @@ import android.view.Surface; import androidx.test.filters.SmallTest; import com.android.compatibility.common.util.ApiLevelUtil; +import com.android.compatibility.common.util.MediaUtils; import org.junit.Assume; import org.junit.Test; @@ -265,7 +266,12 @@ public class EncoderColorAspectsTest extends CodecEncoderTestBase { Assume.assumeTrue("Test introduced with Android 11", sIsAtLeastR); if (mSurfaceMode) { Assume.assumeTrue("Surface mode tests are limited to devices launching with Android T", - FIRST_SDK_IS_AT_LEAST_T); + FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T); + // Few cuttlefish specific color conversion issues were fixed after Android T. + if (MediaUtils.onCuttlefish()) { + Assume.assumeTrue("Color conversion related tests are not valid on cuttlefish " + + "releases through android T", IS_AT_LEAST_U); + } } if (mUseHighBitDepth) { diff --git a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java index a0467a2f692..c82e0392fad 100644 --- a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java +++ b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java @@ -16,14 +16,6 @@ package android.ondevicepersonalization.cts; -import static org.junit.Assert.assertEquals; - -import android.content.Context; -import android.ondevicepersonalization.OnDevicePersonalizationManager; - -import androidx.test.core.app.ApplicationProvider; - -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -33,17 +25,6 @@ import org.junit.runners.JUnit4; */ @RunWith(JUnit4.class) public class OnDevicePersonalizationServiceTest { - private Context mContext; - private OnDevicePersonalizationManager mService; - - @Before - public void setup() throws Exception { - mContext = ApplicationProvider.getApplicationContext(); - mService = mContext.getSystemService(OnDevicePersonalizationManager.class); - } - @Test - public void testVersion() throws Exception { - assertEquals(mService.getVersion(), "1.0"); - } + public void test() throws Exception {} } diff --git a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java index 14f8516b6bc..e6c186c5787 100644 --- a/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java +++ b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java @@ -25,6 +25,7 @@ import android.media.cts.InputSurface; import android.media.cts.OutputSurface; import android.media.cts.TestArgs; import android.opengl.GLES20; +import android.os.Build; import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; @@ -39,6 +40,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import com.android.compatibility.common.util.ApiLevelUtil; import com.android.compatibility.common.util.MediaUtils; import org.junit.Before; @@ -71,6 +73,7 @@ public class DecodeEditEncodeTest { private static final boolean WORK_AROUND_BUGS = false; // avoid fatal codec bugs private static final boolean VERBOSE = false; // lots of logging private static final boolean DEBUG_SAVE_FILE = false; // save copy of encoded movie + private static final boolean IS_AFTER_T = ApiLevelUtil.isAfter(Build.VERSION_CODES.TIRAMISU); // parameters for the encoder private static final int FRAME_RATE = 15; // 15fps @@ -160,13 +163,18 @@ public class DecodeEditEncodeTest { } @Before - public void shouldSkip() { + public void shouldSkip() throws IOException { MediaFormat format = MediaFormat.createVideoFormat(mMediaType, mWidth, mHeight); format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate); format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); assumeTrue(MediaUtils.supports(mEncoderName, format)); assumeTrue(MediaUtils.supports(mDecoderName, format)); + // Few cuttlefish specific color conversion issues were fixed after Android T. + if (MediaUtils.onCuttlefish()) { + assumeTrue("Color conversion related tests are not valid on cuttlefish releases " + + "through android T for format: " + format, IS_AFTER_T); + } } @Parameterized.Parameters(name = "{index}({0}_{1}_{2}_{3}_{4})") diff --git a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java index 7780a1374f2..eff1f512eb2 100644 --- a/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java +++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java @@ -30,6 +30,7 @@ import android.media.cts.OutputSurface; import android.media.cts.SdkMediaCodec; import android.media.cts.TestArgs; import android.opengl.GLES20; +import android.os.Build; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresDevice; import android.util.Log; @@ -37,6 +38,7 @@ import android.util.Log; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.ApiLevelUtil; import com.android.compatibility.common.util.MediaUtils; import java.io.FileOutputStream; @@ -82,6 +84,7 @@ public class EncodeDecodeTest { private static final boolean VERBOSE = false; // lots of logging private static final boolean DEBUG_SAVE_FILE = false; // save copy of encoded movie private static final String DEBUG_FILE_NAME_BASE = "/sdcard/test."; + private static final boolean IS_AFTER_T = ApiLevelUtil.isAfter(Build.VERSION_CODES.TIRAMISU); // parameters for the encoder private static final int FRAME_RATE = 15; // 15fps @@ -283,6 +286,11 @@ public class EncodeDecodeTest { */ public static void runTest(EncodeDecodeTest obj, boolean persisent, boolean useNdk) throws Throwable { + // Few cuttlefish specific color conversion issues were fixed after Android T. + if (MediaUtils.onCuttlefish()) { + assumeTrue("Color conversion related tests are not valid on cuttlefish releases " + + "through android T", IS_AFTER_T); + } SurfaceToSurfaceWrapper wrapper = new SurfaceToSurfaceWrapper(obj, persisent, useNdk); Thread th = new Thread(wrapper, "codec test"); diff --git a/tests/tests/mediastress/AndroidTest.xml b/tests/tests/mediastress/AndroidTest.xml index c6ed5263bc0..215d9271fb0 100644 --- a/tests/tests/mediastress/AndroidTest.xml +++ b/tests/tests/mediastress/AndroidTest.xml @@ -19,6 +19,14 @@ <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" /> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device --> + <option name="set-test-harness" value="false" /> + <option name="screen-always-on" value="on" /> + <option name="screen-adaptive-brightness" value="off" /> + <option name="disable-audio" value="false"/> + <option name="screen-saver" value="off"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> <option name="target" value="host" /> <option name="config-filename" value="cts" /> diff --git a/tests/tests/permission/Android.bp b/tests/tests/permission/Android.bp index 5c861b51e30..91a9dfcfafe 100644 --- a/tests/tests/permission/Android.bp +++ b/tests/tests/permission/Android.bp @@ -113,6 +113,8 @@ android_test { ":CtsStorageEscalationApp29Full", ":CtsStorageEscalationApp29Scoped", ":CtsVictimPermissionDefinerApp", + ":CtsAppThatRequestsSystemAlertWindow22", + ":CtsAppThatRequestsSystemAlertWindow23", ], per_testcase_directory: true, } diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml index 56a72c9b3f2..eafc6a6389c 100644 --- a/tests/tests/permission/AndroidTest.xml +++ b/tests/tests/permission/AndroidTest.xml @@ -92,6 +92,8 @@ <option name="push" value="CtsStorageEscalationApp29Full.apk->/data/local/tmp/cts/permissions/CtsStorageEscalationApp29Full.apk" /> <option name="push" value="CtsStorageEscalationApp29Scoped.apk->/data/local/tmp/cts/permissions/CtsStorageEscalationApp29Scoped.apk" /> <option name="push" value="CtsAppThatHasNotificationListener.apk->/data/local/tmp/cts/permissions/CtsAppThatHasNotificationListener.apk" /> + <option name="push" value="CtsAppThatRequestsSystemAlertWindow22.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsSystemAlertWindow22.apk" /> + <option name="push" value="CtsAppThatRequestsSystemAlertWindow23.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsSystemAlertWindow23.apk" /> </target_preparer> <!-- Remove additional apps if installed --> diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow22/Android.bp b/tests/tests/permission/AppThatRequestSystemAlertWindow22/Android.bp new file mode 100644 index 00000000000..43cc9de97a0 --- /dev/null +++ b/tests/tests/permission/AppThatRequestSystemAlertWindow22/Android.bp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2022 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: "CtsAppThatRequestsSystemAlertWindow22", + target_sdk_version: "22", + certificate: ":cts-testkey2", + min_sdk_version: "22", + test_suites: [ + "cts", + "general-tests", + "mts-permission", + "sts", + ], +} diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml b/tests/tests/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml new file mode 100644 index 00000000000..8b85b132a61 --- /dev/null +++ b/tests/tests/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2022 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.permission3.cts.usesystemalertwindowpermission"> + + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> +</manifest> diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow23/Android.bp b/tests/tests/permission/AppThatRequestSystemAlertWindow23/Android.bp new file mode 100644 index 00000000000..403257d4554 --- /dev/null +++ b/tests/tests/permission/AppThatRequestSystemAlertWindow23/Android.bp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2022 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: "CtsAppThatRequestsSystemAlertWindow23", + target_sdk_version: "23", + certificate: ":cts-testkey2", + min_sdk_version: "23", + test_suites: [ + "cts", + "general-tests", + "mts-permission", + "sts", + ], +} diff --git a/tests/tests/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml b/tests/tests/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml new file mode 100644 index 00000000000..8b85b132a61 --- /dev/null +++ b/tests/tests/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2022 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.permission3.cts.usesystemalertwindowpermission"> + + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> +</manifest> diff --git a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java index f88b7ecbbaa..67bb9166370 100644 --- a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java +++ b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java @@ -433,7 +433,7 @@ public class BaseNotificationListenerCheckTest { * @return The notification or `null` if there is none */ protected StatusBarNotification getNotification(boolean cancelNotification) throws Throwable { - return NotificationUtils.getNotificationForPackageAndId( + return NotificationListenerUtils.getNotificationForPackageAndId( PERMISSION_CONTROLLER_PKG, NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID, cancelNotification); @@ -444,6 +444,6 @@ public class BaseNotificationListenerCheckTest { */ protected void clearNotifications() throws Throwable { // Clear notification if present - NotificationUtils.clearNotificationsForPackage(PERMISSION_CONTROLLER_PKG); + NotificationListenerUtils.cancelNotifications(PERMISSION_CONTROLLER_PKG); } } diff --git a/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt b/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt index 0ee8a3db57a..dffefe790f9 100644 --- a/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt +++ b/tests/tests/permission/src/android/permission/cts/NotificationListenerUtils.kt @@ -16,16 +16,15 @@ package android.permission.cts -import android.service.notification.StatusBarNotification -import org.junit.Assert import android.permission.cts.TestUtils.ensure import android.permission.cts.TestUtils.eventually +import android.service.notification.StatusBarNotification +import org.junit.Assert object NotificationListenerUtils { private const val NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS = 5000L private const val NOTIFICATION_WAIT_MILLIS = 2000L - private val notificationService = NotificationListener.getInstance() @JvmStatic fun assertEmptyNotification(packageName: String, notificationId: Int) { @@ -47,6 +46,7 @@ object NotificationListenerUtils { @JvmStatic fun cancelNotification(packageName: String, notificationId: Int) { + val notificationService = NotificationListener.getInstance() val notification = getNotification(packageName, notificationId) if (notification != null) { notificationService.cancelNotification(notification.key) @@ -58,6 +58,7 @@ object NotificationListenerUtils { @JvmStatic fun cancelNotifications(packageName: String) { + val notificationService = NotificationListener.getInstance() val notifications = getNotifications(packageName) if (notifications.isNotEmpty()) { notifications.forEach { notification -> @@ -79,6 +80,7 @@ object NotificationListenerUtils { @JvmStatic fun getNotifications(packageName: String): List<StatusBarNotification> { val notifications: MutableList<StatusBarNotification> = ArrayList() + val notificationService = NotificationListener.getInstance() for (notification in notificationService.activeNotifications) { if (notification.packageName == packageName) { notifications.add(notification) @@ -86,4 +88,32 @@ object NotificationListenerUtils { } return notifications } -}
\ No newline at end of file + + /** + * Get a notification listener notification that is currently visible. + * + * @param cancelNotification if `true` the notification is canceled inside this method + * @return The notification or `null` if there is none + */ + @JvmStatic + @Throws(Throwable::class) + fun getNotificationForPackageAndId( + pkg: String, + id: Int, + cancelNotification: Boolean + ): StatusBarNotification? { + val notifications: List<StatusBarNotification> = getNotifications(pkg) + if (notifications.isEmpty()) { + return null + } + for (notification in notifications) { + if (notification.id == id) { + if (cancelNotification) { + cancelNotification(pkg, id) + } + return notification + } + } + return null + } +} diff --git a/tests/tests/permission/src/android/permission/cts/NotificationUtils.kt b/tests/tests/permission/src/android/permission/cts/NotificationUtils.kt deleted file mode 100644 index 7c50837f82b..00000000000 --- a/tests/tests/permission/src/android/permission/cts/NotificationUtils.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2022 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 - -import android.service.notification.StatusBarNotification -import org.junit.Assert -import java.util.concurrent.TimeUnit - -/** - * Utility methods to interact with NotificationManager through the CTS NotificationListenerService - * to get or clear notifications. - */ -object NotificationUtils { - - private val NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5) - - /** - * Get a notification listener notification that is currently visible. - * - * @param cancelNotification if `true` the notification is canceled inside this method - * @return The notification or `null` if there is none - */ - @JvmStatic - @Throws(Throwable::class) - fun getNotificationForPackageAndId( - pkg: String, - id: Int, - cancelNotification: Boolean - ): StatusBarNotification? { - val notificationService = NotificationListener.getInstance() - val notifications: List<StatusBarNotification> = getNotificationsForPackage(pkg) - if (notifications.isEmpty()) { - return null - } - for (notification in notifications) { - if (notification.id == id) { - if (cancelNotification) { - clearNotification(notification) - } - return notification - } - } - return null - } - - /** - * Clears all currently visible notifications for a specified package. - */ - @JvmStatic - @Throws(Throwable::class) - fun clearNotificationsForPackage(pkg: String) { - val notifications: List<StatusBarNotification> = getNotificationsForPackage(pkg) - if (notifications.isEmpty()) { - return - } - - clearNotifications(notifications) - } - - /** Clears the specified notification and ensures (asserts) it was removed */ - @JvmStatic - @Throws(Throwable::class) - fun clearNotification(notification: StatusBarNotification) { - val notificationService = NotificationListener.getInstance() - notificationService.cancelNotification(notification.key) - - // Wait for notification to get canceled - TestUtils.eventually({ - Assert.assertFalse( - listOf(*notificationService.activeNotifications) - .contains(notification) - ) - }, NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS) - } - - private fun clearNotifications(notifications: List<StatusBarNotification>) { - val notificationService = NotificationListener.getInstance() - notifications.forEach { notificationService.cancelNotification(it.key) } - - // Wait for notification to get canceled - TestUtils.eventually({ - val activeNotifications: List<StatusBarNotification> = - listOf(*notificationService.activeNotifications) - Assert.assertFalse( - activeNotifications.any { notifications.contains(it) } - ) - }, NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS) - } - - /** - * Get all notifications associated with a given package that are currently visible. - * @param pkg Package for which to filter the notifications by - * @return [List] of [StatusBarNotification] - */ - @Throws(Exception::class) - private fun getNotificationsForPackage(pkg: String): List<StatusBarNotification> { - val notificationService = NotificationListener.getInstance() - val notifications: MutableList<StatusBarNotification> = ArrayList() - for (notification in notificationService.activeNotifications) { - if (notification.packageName == pkg) { - notifications.add(notification) - } - } - return notifications - } -}
\ No newline at end of file diff --git a/tests/tests/permission/src/android/permission/cts/RevokeSawPermissionTest.kt b/tests/tests/permission/src/android/permission/cts/RevokeSawPermissionTest.kt new file mode 100644 index 00000000000..fe5373e1c37 --- /dev/null +++ b/tests/tests/permission/src/android/permission/cts/RevokeSawPermissionTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 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 + +import android.content.pm.PackageManager +import android.platform.test.annotations.AsbSecurityTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.compatibility.common.util.SystemUtil +import org.junit.After +import org.junit.Assert +import org.junit.Test + +private val APP_PKG_NAME = "android.permission3.cts.usesystemalertwindowpermission" +private val APK_22 = "/data/local/tmp/cts/permissions/" + + "CtsAppThatRequestsSystemAlertWindow22.apk" +private val APK_23 = "/data/local/tmp/cts/permissions/" + + "CtsAppThatRequestsSystemAlertWindow23.apk" + +class RevokeSawPermissionTest { + + fun installApp(apk: String) { + SystemUtil.runShellCommand("pm install -r $apk") + } + + @After + fun uninstallApp() { + SystemUtil.runShellCommand("pm uninstall $APP_PKG_NAME") + } + + @AsbSecurityTest(cveBugId = [247512334L]) + @Test + fun testPre23AppsWithSystemAlertWindowGetDeniedOnUpgrade() { + installApp(APK_22) + assertAppHasPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, true) + installApp(APK_23) + assertAppHasPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, false) + } + + private fun assertAppHasPermission(permissionName: String, expectPermission: Boolean) { + Assert.assertEquals( + if (expectPermission) { + PackageManager.PERMISSION_GRANTED + } else { + PackageManager.PERMISSION_DENIED + }, + InstrumentationRegistry.getInstrumentation().getTargetContext().packageManager + .checkPermission(permissionName, APP_PKG_NAME) + ) + } +} diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt index d5bd49bdf37..47faaa65c46 100644 --- a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt +++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt @@ -92,8 +92,12 @@ class CameraMicIndicatorsPermissionTest { private val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) private val isCar = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) - private val micLabel = getPermissionControllerString(MIC_LABEL_NAME) - private val cameraLabel = getPermissionControllerString(CAMERA_LABEL_NAME) + private val safetyCenterMicLabel = getPermissionControllerString(MIC_LABEL_NAME) + private val safetyCenterCameraLabel = getPermissionControllerString(CAMERA_LABEL_NAME) + private val cameraLabel = packageManager.getPermissionGroupInfo( + Manifest.permission_group.CAMERA, 0).loadLabel(packageManager).toString().toLowerCase() + private val micLabel = packageManager.getPermissionGroupInfo( + Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString().toLowerCase() private var wasEnabled = false private var isScreenOn = false private var screenTimeoutBeforeTest: Long = 0L @@ -120,8 +124,6 @@ class CameraMicIndicatorsPermissionTest { Settings.System.putLong( context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 1800000L ) - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, - SAFETY_CENTER_ENABLED, false.toString(), false) } if (!isScreenOn) { @@ -407,22 +409,28 @@ class CameraMicIndicatorsPermissionTest { return@eventually } if (useMic) { + var micIdentifier: String var iconView = if (safetyCenterEnabled) { - waitFindObjectOrNull(By.text(micLabel)) + micIdentifier = safetyCenterMicLabel + waitFindObject(By.text(micIdentifier)) } else { - uiDevice.findObject(UiSelector().descriptionContains(micLabel)) + micIdentifier = micLabel + waitFindObject(By.descContains(micIdentifier)) } - assertNotNull("View with description $micLabel not found", iconView) + assertNotNull("View with text/description $micIdentifier not found", iconView) } if (useCamera) { + var camIdentifier: String var iconView = if (safetyCenterEnabled) { - waitFindObjectOrNull(By.text(cameraLabel)) + camIdentifier = safetyCenterCameraLabel + waitFindObject(By.text(camIdentifier)) } else { - uiDevice.findObject(UiSelector().descriptionContains(cameraLabel)) + camIdentifier = cameraLabel + waitFindObject(By.descContains(camIdentifier)) } - assertNotNull("View with text $APP_LABEL not found", iconView) + assertNotNull("View with text/description $camIdentifier not found", iconView) } - var appView = waitFindObjectOrNull(By.textContains(APP_LABEL)) + var appView = waitFindObject(By.textContains(APP_LABEL)) assertNotNull("View with text $APP_LABEL not found", appView) if (safetyCenterEnabled) { assertTrue("Did not find safety center views", @@ -456,11 +464,11 @@ class CameraMicIndicatorsPermissionTest { } if (safetyCenterEnabled) { - var micView = waitFindObjectOrNull(By.text(micLabel)) + var micView = waitFindObject(By.text(safetyCenterMicLabel)) assertNotNull("View with text $micLabel not found", micView) - var camView = waitFindObjectOrNull(By.text(cameraLabel)) + var camView = waitFindObject(By.text(safetyCenterCameraLabel)) assertNotNull("View with text $cameraLabel not found", camView) - var shellView = waitFindObjectOrNull(By.textContains(shellLabel)) + var shellView = waitFindObject(By.textContains(shellLabel)) assertNotNull("View with text $shellLabel not found", shellView) } else { val usageViews = uiDevice.findObjects(By.res(PRIVACY_ITEM_ID)) @@ -516,9 +524,9 @@ class CameraMicIndicatorsPermissionTest { assumeTrue(isSafetyCenterEnabled) } - protected fun waitFindObjectOrNull(selector: BySelector): UiObject2? { + protected fun waitFindObject(selector: BySelector): UiObject2? { waitForIdle() - return findObjectWithRetry({ t -> UiAutomatorUtils.waitFindObjectOrNull(selector, t) }) + return findObjectWithRetry({ t -> UiAutomatorUtils.waitFindObject(selector, t) }) } private fun findObjectWithRetry( @@ -551,4 +559,4 @@ class CameraMicIndicatorsPermissionTest { throw RuntimeException(e) } } -}
\ No newline at end of file +} diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp index 0cf3dd54c6b..9f4cda5752c 100644 --- a/tests/tests/security/Android.bp +++ b/tests/tests/security/Android.bp @@ -33,6 +33,7 @@ android_test { "compatibility-common-util-devicesidelib", "guava", "platform-test-annotations", + "permission-test-util-lib", "sts-device-util", "hamcrest-library", "NeneInternal", @@ -80,7 +81,17 @@ android_test { ":CtsDeviceInfo", ":RolePermissionOverrideTestApp", ":SplitBluetoothPermissionTestApp", - ], + ":CtsPermissionBackupAppCert1", + ":CtsPermissionBackupAppCert1Dup", + ":CtsPermissionBackupAppCert2", + ":CtsPermissionBackupAppCert3", + ":CtsPermissionBackupAppCert4", + ":CtsPermissionBackupAppCert12", + ":CtsPermissionBackupAppCert12Dup", + ":CtsPermissionBackupAppCert34", + ":CtsPermissionBackupAppCert123", + ":CtsPermissionBackupAppCert4History124", + ], } android_test_helper_app { @@ -89,6 +100,95 @@ android_test_helper_app { manifest: "testdata/packageinstallertestapp.xml", } +android_test_helper_app { + name: "CtsPermissionBackupAppCert1", + min_sdk_version: "30", + certificate: ":permission-test-cert-1", + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert1Dup", + min_sdk_version: "30", + certificate: ":permission-test-cert-1", + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert2", + min_sdk_version: "30", + certificate: ":permission-test-cert-2", + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert3", + min_sdk_version: "30", + certificate: ":permission-test-cert-3", + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert4", + min_sdk_version: "30", + certificate: ":permission-test-cert-4", + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert12", + min_sdk_version: "30", + certificate: ":permission-test-cert-1", + additional_certificates: [ + ":permission-test-cert-2", + ], + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert12Dup", + min_sdk_version: "30", + certificate: ":permission-test-cert-1", + additional_certificates: [ + ":permission-test-cert-2", + ], + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert34", + min_sdk_version: "30", + certificate: ":permission-test-cert-3", + additional_certificates: [ + ":permission-test-cert-4", + ], + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert123", + min_sdk_version: "30", + certificate: ":permission-test-cert-1", + additional_certificates: [ + ":permission-test-cert-2", + ":permission-test-cert-3", + ], + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + +android_test_helper_app { + name: "CtsPermissionBackupAppCert4History124", + min_sdk_version: "30", + certificate: ":permission-test-cert-4", + additional_certificates: [ + ":permission-test-cert-1", + + ], + rotationMinSdkVersion: "30", + lineage: ":permission-test-cert-with-rotation-history", + manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml", +} + android_app_certificate { name: "security_cts_test_certificate", certificate: "security_cts_test_cert", @@ -98,3 +198,30 @@ android_test_helper_app { name: "RolePermissionOverrideTestApp", manifest: "testdata/rolepermissionoverridetestapp.xml", } + +android_app_certificate { + name: "permission-test-cert-1", + certificate: "test-cert-1", +} + +android_app_certificate { + name: "permission-test-cert-2", + certificate: "test-cert-2", +} + +android_app_certificate { + name: "permission-test-cert-3", + certificate: "test-cert-3", +} + +android_app_certificate { + name: "permission-test-cert-4", + certificate: "test-cert-4", +} + +filegroup { + name: "permission-test-cert-with-rotation-history", + srcs: [ + "test-cert-with-1-2-4-in-rotation-history", + ], +} diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml index 4ddeca0e23c..72a110b40d6 100644 --- a/tests/tests/security/AndroidManifest.xml +++ b/tests/tests/security/AndroidManifest.xml @@ -18,6 +18,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.security.cts"> + <permission-tree android:name="com.android.cts"/> + <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/> <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> @@ -201,9 +203,34 @@ </intent-filter> </activity> + <receiver android:name="android.security.cts.CVE_2022_20420.PocDeviceAdminReceiver" + android:exported="true" + android:permission="android.permission.BIND_DEVICE_ADMIN"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_admin_CVE_2022_20420" /> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + </intent-filter> + </receiver> + <activity android:name="android.security.cts.ActivityManagerTest$ActivityOptionsActivity" /> <activity android:name="android.security.cts.ActivityManagerTest$BaseActivity" /> + <provider android:name="android.security.cts.CVE_2022_20358.PocContentProvider" + android:authorities="android.security.cts.CVE_2022_20358.provider" + android:enabled="true" + android:exported="true" /> + + <service android:name="android.security.cts.CVE_2022_20358.PocSyncService" + android:enabled="true" + android:exported="true"> + <intent-filter> + <action android:name="android.content.SyncAdapter" /> + </intent-filter> + <meta-data android:name="android.content.SyncAdapter" + android:resource="@xml/syncadapter" /> + </service> + </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/tests/tests/security/AndroidTest.xml b/tests/tests/security/AndroidTest.xml index 9bd5eb7e0a8..73e6bc7ece8 100644 --- a/tests/tests/security/AndroidTest.xml +++ b/tests/tests/security/AndroidTest.xml @@ -52,6 +52,16 @@ <option name="cleanup" value="true" /> <option name="push" value="RolePermissionOverrideTestApp.apk->/data/local/tmp/cts/security/RolePermissionOverrideTestApp.apk" /> <option name="push" value="SplitBluetoothPermissionTestApp.apk->/data/local/tmp/cts/security/SplitBluetoothPermissionTestApp.apk" /> + <option name="push" value="CtsPermissionBackupAppCert1.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert1.apk" /> + <option name="push" value="CtsPermissionBackupAppCert1Dup.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert1Dup.apk" /> + <option name="push" value="CtsPermissionBackupAppCert2.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert2.apk" /> + <option name="push" value="CtsPermissionBackupAppCert3.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert3.apk" /> + <option name="push" value="CtsPermissionBackupAppCert4.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert4.apk" /> + <option name="push" value="CtsPermissionBackupAppCert12.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert12.apk" /> + <option name="push" value="CtsPermissionBackupAppCert12Dup.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert12Dup.apk" /> + <option name="push" value="CtsPermissionBackupAppCert123.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert123.apk" /> + <option name="push" value="CtsPermissionBackupAppCert34.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert34.apk" /> + <option name="push" value="CtsPermissionBackupAppCert4History124.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert4History124.apk" /> </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > @@ -61,4 +71,9 @@ <option name="test-timeout" value="900000" /> <option name="hidden-api-checks" value="false" /> </test> + + <target_preparer class="android.cts.backup.BackupPreparer"> + <option name="enable-backup-if-needed" value="true" /> + <option name="select-local-transport" value="true" /> + </target_preparer> </configuration> diff --git a/tests/tests/security/res/raw/cve_2022_25669.3gp b/tests/tests/security/res/raw/cve_2022_25669.3gp Binary files differnew file mode 100644 index 00000000000..f5ba05ad9aa --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_25669.3gp diff --git a/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml b/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml new file mode 100644 index 00000000000..cb567e31d4e --- /dev/null +++ b/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 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. + --> +<device-admin> + <uses-policies> + </uses-policies> +</device-admin> diff --git a/tests/tests/security/res/xml/syncadapter.xml b/tests/tests/security/res/xml/syncadapter.xml new file mode 100644 index 00000000000..478fad5327f --- /dev/null +++ b/tests/tests/security/res/xml/syncadapter.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 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. + --> +<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" + android:accountType="CVE_2022_20358_acc" + android:isAlwaysSyncable="true" /> diff --git a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java index 397c0129661..7bb74ffee8c 100644 --- a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java +++ b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java @@ -18,28 +18,24 @@ package android.security.cts; import static org.junit.Assert.fail; -import android.app.Activity; +import android.annotation.SuppressLint; import android.os.BaseBundle; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.platform.test.annotations.AsbSecurityTest; import android.view.AbsSavedState; import android.view.View; -import android.view.View.BaseSavedState; -import android.annotation.SuppressLint; + import androidx.test.runner.AndroidJUnit4; import com.android.sts.common.util.StsExtraBusinessLogicTestCase; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.util.Random; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; -import android.security.cts.R; -import android.platform.test.annotations.AsbSecurityTest; +import java.lang.reflect.Field; +import java.util.Random; @RunWith(AndroidJUnit4.class) public class AmbiguousBundlesTest extends StsExtraBusinessLogicTestCase { @@ -601,6 +597,95 @@ public class AmbiguousBundlesTest extends StsExtraBusinessLogicTestCase { testAmbiguator(ambiguator); } + /* + * b/240138294 + */ + @AsbSecurityTest(cveBugId = 240138294) + @Test + public void test_lazyValueNegativeLength() throws Exception { + Ambiguator ambiguator = new Ambiguator() { + @Override + public Bundle make(Bundle preReSerialize, Bundle postReSerialize) { + // Find key that has hash below everything else + Random random = new Random(1234); + int minHash = 0; + for (String s : preReSerialize.keySet()) { + minHash = Math.min(minHash, s.hashCode()); + } + for (String s : postReSerialize.keySet()) { + minHash = Math.min(minHash, s.hashCode()); + } + + String negativePrefix, positivePrefix; + // When read as value, jump back to the start of the header (8 bytes) + negativePrefix = getStringEncodingInt(-8); + // Size of the malicious bundle before the 'cmd' key + positivePrefix = getStringEncodingInt(48); + + String key1, key2, key3; + int key1Hash, key2Hash, key3Hash; + + do { + key1 = randomString(random); + // 16 characters total, will be read as type parcelable array when + // read as value + key2 = negativePrefix + randomString(random, 14); + key3 = positivePrefix + randomString(random, 14); + key1Hash = key1.hashCode(); + key2Hash = key3.hashCode(); // 2 and 3 are swapped + key3Hash = key2.hashCode(); + } while (!(key1Hash < key2Hash && key2Hash < key3Hash && key3Hash < minHash)); + + // Pad bundles - ensures keys are in right hash order + padBundle(postReSerialize, preReSerialize.size() + 2, minHash, random); + padBundle(preReSerialize, postReSerialize.size() - 2, minHash, random); + + // Write bundle + Parcel parcel = Parcel.obtain(); + + int sizePosition = parcel.dataPosition(); + parcel.writeInt(0); + parcel.writeInt(BUNDLE_MAGIC_NATIVE); + int startPosition = parcel.dataPosition(); + + parcel.writeInt(preReSerialize.size() + 3); // Num key-value pairs + + parcel.writeString(key1); // Key 1 + parcel.writeString(key2); // Value 1/Key 2 + parcel.writeInt(VAL_NULL); + parcel.writeString(key3); + parcel.writeInt(VAL_BUNDLE); + parcel.writeBundle(postReSerialize); // Value 3 + + // Data from preReSerialize bundle + writeBundleSkippingHeaders(parcel, preReSerialize); + + // Fix up bundle size + int bundleDataSize = parcel.dataPosition() - startPosition; + parcel.setDataPosition(sizePosition); + parcel.writeInt(bundleDataSize); + + parcel.setDataPosition(0); + Bundle bundle = parcel.readBundle(); + parcel.recycle(); + return bundle; + } + + private String getStringEncodingInt(int i) { + Parcel parcel = Parcel.obtain(); + parcel.writeInt(2); + parcel.writeInt(i); + parcel.writeInt(0); + parcel.setDataPosition(0); + String s = parcel.readString(); + parcel.recycle(); + return s; + } + }; + + testAmbiguator(ambiguator); + } + private void testAmbiguator(Ambiguator ambiguator) { Bundle bundle; Bundle verifyMe = new Bundle(); @@ -653,6 +738,7 @@ public class AmbiguousBundlesTest extends StsExtraBusinessLogicTestCase { protected static final int PROCSTATS_SPARSE_MAPPING_TABLE_ARRAY_SIZE = 4096; protected static final int BUNDLE_MAGIC = 0x4C444E42; + protected static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N' protected static final int INNER_BUNDLE_PADDING = 1; protected Field parcelledDataField; @@ -711,8 +797,12 @@ public class AmbiguousBundlesTest extends StsExtraBusinessLogicTestCase { } protected static String randomString(Random random) { + return randomString(random, 6); + } + + protected static String randomString(Random random, int len) { StringBuilder b = new StringBuilder(); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < len; i++) { b.append((char)(' ' + random.nextInt('~' - ' ' + 1))); } return b.toString(); diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java new file mode 100644 index 00000000000..b1ff1688ced --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20358; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.accounts.Account; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Context; +import android.content.ISyncAdapter; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20358 extends StsExtraBusinessLogicTestCase implements ServiceConnection { + static final int TIMEOUT_SEC = 10; + Semaphore mWaitResultServiceConn; + boolean mIsAssumeFail = false; + String mAssumeFailMsg = ""; + + @AsbSecurityTest(cveBugId = 203229608) + @Test + public void testPocCVE_2022_20358() { + try { + // Bind to the PocSyncService + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + Context context = instrumentation.getContext(); + Intent intent = new Intent(context, PocSyncService.class); + intent.setAction("android.content.SyncAdapter"); + CompletableFuture<String> callbackReturn = new CompletableFuture<>(); + RemoteCallback cb = new RemoteCallback((Bundle result) -> { + callbackReturn.complete(result.getString("fail")); + }); + intent.putExtra("callback", cb); + context.bindService(intent, this, Context.BIND_AUTO_CREATE); + + // Wait for some result from the PocSyncService + mWaitResultServiceConn = new Semaphore(0); + assumeTrue(mWaitResultServiceConn.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)); + assumeTrue(mAssumeFailMsg, !mIsAssumeFail); + + // Wait for a result to be set from onPerformSync() of PocSyncAdapter + callbackReturn.get(TIMEOUT_SEC, TimeUnit.SECONDS); + + // In presence of vulnerability, the above call succeeds and TimeoutException is not + // triggered so failing the test + fail("Vulnerable to b/203229608!!"); + } catch (Exception e) { + if (e instanceof TimeoutException) { + // The fix is present so returning from here + return; + } + assumeNoException(e); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + try { + if (mWaitResultServiceConn == null) { + mWaitResultServiceConn = new Semaphore(0); + } + ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service); + Account account = new Account("CVE_2022_20358_user", "CVE_2022_20358_acc"); + adapter.startSync(null, "android.security.cts.CVE_2022_20358.provider", account, null); + mWaitResultServiceConn.release(); + } catch (Exception e) { + try { + mWaitResultServiceConn.release(); + mAssumeFailMsg = e.getMessage(); + mIsAssumeFail = true; + } catch (Exception ex) { + // ignore all exceptions + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + try { + mWaitResultServiceConn.release(); + } catch (Exception e) { + // ignore all exceptions + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java new file mode 100644 index 00000000000..0bc8c2c5fed --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20358; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class PocContentProvider extends ContentProvider { + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java new file mode 100644 index 00000000000..08fbf92d8e5 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20358; + +import android.accounts.Account; +import android.app.Service; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.Context; +import android.content.Intent; +import android.content.SyncResult; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteCallback; + +public class PocSyncService extends Service { + private static PocSyncAdapter sSyncAdapter = null; + private static final Object sSyncAdapterLock = new Object(); + RemoteCallback mCb; + + @Override + public void onCreate() { + try { + synchronized (sSyncAdapterLock) { + if (sSyncAdapter == null) { + sSyncAdapter = new PocSyncAdapter(this); + } + } + } catch (Exception e) { + // ignore all exceptions + } + } + + @Override + public IBinder onBind(Intent intent) { + try { + mCb = (RemoteCallback) intent.getExtra("callback"); + } catch (Exception e) { + // ignore all exceptions + } + return sSyncAdapter.getSyncAdapterBinder(); + } + + public class PocSyncAdapter extends AbstractThreadedSyncAdapter { + + public PocSyncAdapter(Context context) { + super(context, false); + } + + @Override + public void onPerformSync(Account account, Bundle extras, String authority, + ContentProviderClient provider, SyncResult syncResult) { + try { + if (account.type.equals("CVE_2022_20358_acc") + && account.name.equals("CVE_2022_20358_user")) { + Bundle res = new Bundle(); + res.putString("fail", ""); + mCb.sendResult(res); + } + } catch (Exception e) { + // ignore all exceptions + } + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java b/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java new file mode 100644 index 00000000000..35d576e5142 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20420; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.ActivityManager; +import android.app.UiAutomation; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IDeviceIdleController; +import android.os.PowerExemptionManager; +import android.os.Process; +import android.os.ServiceManager; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20420 extends StsExtraBusinessLogicTestCase { + private static final int TIMEOUT_MS = 10000; + private static final int USER_ID = 0; + private Context mContext; + private DevicePolicyManager mPolicyManager; + private ComponentName mComponentName; + private UiAutomation mAutomation; + + @After + public void tearDown() { + try { + mAutomation.dropShellPermissionIdentity(); + mPolicyManager.removeActiveAdmin(mComponentName); + } catch (Exception ignored) { + // ignore all exceptions as the test has been completed. + } + } + + @AsbSecurityTest(cveBugId = 238377411) + @Test + public void testDeviceAdminAppRestricted() { + try { + // Add test app to Power Save Whitelist. + mContext = getInstrumentation().getTargetContext(); + mAutomation = getInstrumentation().getUiAutomation(); + mAutomation.adoptShellPermissionIdentity(android.Manifest.permission.DEVICE_POWER, + android.Manifest.permission.MANAGE_DEVICE_ADMINS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + IDeviceIdleController mDeviceIdleService = + IDeviceIdleController.Stub.asInterface(ServiceManager.getService("deviceidle")); + mDeviceIdleService.addPowerSaveWhitelistApp(mContext.getPackageName()); + + // Set test app as "Active Admin". + mPolicyManager = mContext.getSystemService(DevicePolicyManager.class); + mComponentName = new ComponentName(mContext, PocDeviceAdminReceiver.class); + mPolicyManager.setActiveAdmin(mComponentName, true, USER_ID); + CompletableFuture<Boolean> future = new CompletableFuture<>(); + BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + future.complete(true); + } + }; + mContext.registerReceiver(broadcastReceiver, + new IntentFilter("broadcastCVE_2022_20420")); + future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + + // Call vulnerable function getBackgroundRestrictionExemptionReason() + ActivityManager activityManager = mContext.getSystemService(ActivityManager.class); + int reason = activityManager.getBackgroundRestrictionExemptionReason(Process.myUid()); + assumeTrue( + "Reason code other than REASON_ACTIVE_DEVICE_ADMIN/REASON_ALLOWLISTED_PACKAGE" + + " returned by getBackgroundRestrictionExemptionReason() = " + reason, + reason == PowerExemptionManager.REASON_ACTIVE_DEVICE_ADMIN + || reason == PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE); + assertFalse("Vulnerable to b/238377411 !!", + reason == PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java b/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java new file mode 100644 index 00000000000..c9c1b6f13be --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20420; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; + +public class PocDeviceAdminReceiver extends DeviceAdminReceiver { + + @Override + public void onEnabled(Context context, Intent intent) { + try { + context.sendBroadcast(new Intent("broadcastCVE_2022_20420")); + } catch (Exception e) { + // ignore all exceptions. + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java new file mode 100644 index 00000000000..af581a1a54d --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 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. + */ + +// This PoC has been written taking reference from: +// File: frameworks/base/core/tests/coretests/src/android/os/BundleTest.java +// Function: readFromParcelWithRwHelper_whenThrowingAndDefusing_returnsNull() + +package android.security.cts.CVE_2022_20452; + +import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeNoException; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20452 extends StsExtraBusinessLogicTestCase { + + @AsbSecurityTest(cveBugId = 240138318) + @Test + public void testPocCVE_2022_20452() { + try { + // Create a bundle with some parcelable object and a random string + Bundle bundle = new Bundle(); + Parcelable parcelable = new CustomParcelable(); + bundle.putParcelable("keyParcelable", parcelable); + bundle.putString("keyStr", "valStr"); + + // Read bundle contents into a parcel and also set read write helper for the parcel + Parcel parcelledBundle = Parcel.obtain(); + bundle.writeToParcel(parcelledBundle, 0); + parcelledBundle.setDataPosition(0); + parcelledBundle.setReadWriteHelper(new Parcel.ReadWriteHelper()); + + // First set 'shouldDefuse' to true, then read contents of parcel into a bundle. + // In presence of fix, this will cause a ClassNotFoundException because bundle will not + // be able to find the class for 'CustomParcelable' as the class loader is not set, so + // Parcel will not be read properly and the code will return without reading the string. + Bundle.setShouldDefuse(true); + Bundle testBundle = new Bundle(); + testBundle.readFromParcel(parcelledBundle); + + // If the vulnerability is active, we will be able to read string from bundle. + assertNull("Vulnerable to b/240138318 !!", testBundle.getString("keyStr")); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java new file mode 100644 index 00000000000..f076eee6535 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 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.security.cts.CVE_2022_20452; + +import android.os.Parcel; +import android.os.Parcelable; + +public class CustomParcelable implements Parcelable { + private boolean mDummyValue = true; + + CustomParcelable() { + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeBoolean(mDummyValue); + } + + public static final Creator<CustomParcelable> CREATOR = + new Creator<CustomParcelable>() { + @Override + public CustomParcelable createFromParcel(Parcel in) { + return new CustomParcelable(); + } + + @Override + public CustomParcelable[] newArray(int size) { + return new CustomParcelable[size]; + } + }; +} diff --git a/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt b/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt new file mode 100644 index 00000000000..11709395f75 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt @@ -0,0 +1,813 @@ +/* + * Copyright (C) 2018 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.security.cts + +import android.Manifest.permission.* +import android.app.AppOpsManager +import android.content.pm.PackageManager.* +import android.os.ParcelFileDescriptor +import android.permission.cts.PermissionUtils.grantPermission +import android.platform.test.annotations.AppModeFull +import android.platform.test.annotations.AsbSecurityTest +import androidx.test.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compatibility.common.util.BackupUtils +import com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN +import com.android.compatibility.common.util.BusinessLogicTestCase +import com.android.compatibility.common.util.ShellUtils.runShellCommand +import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity +import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity +import com.android.sts.common.util.StsExtraBusinessLogicTestCase +import java.io.InputStream +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Tests that permissions for backed up apps are restored only after checking that their signing + * certificates are compared. + * + * @see [com.android.permissioncontroller.permission.service.BackupHelper] + */ +@AppModeFull +@RunWith(AndroidJUnit4::class) +class PermissionBackupCertificateCheckTest : StsExtraBusinessLogicTestCase() { + private val backupUtils: BackupUtils = + object : BackupUtils() { + override fun executeShellCommand(command: String): InputStream { + val pfd = + BusinessLogicTestCase.getInstrumentation() + .uiAutomation + .executeShellCommand(command) + return ParcelFileDescriptor.AutoCloseInputStream(pfd) + } + } + + private var isBackupSupported = false + + private val targetContext = InstrumentationRegistry.getTargetContext() + + @Before + fun setUp() { + val packageManager = BusinessLogicTestCase.getInstrumentation().context.packageManager + isBackupSupported = + (packageManager != null && packageManager.hasSystemFeature(FEATURE_BACKUP)) + + if (isBackupSupported) { + assertTrue("Backup not enabled", backupUtils.isBackupEnabled) + assertTrue("LocalTransport not selected", backupUtils.isLocalTransportSelected) + backupUtils.executeShellCommandSync("setprop log.tag.$APP_LOG_TAG VERBOSE") + } + } + + @After + fun tearDown() { + uninstallIfInstalled(APP) + clearFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET) + clearFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET) + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has the + * same certificate as the backed up app. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_sameCert_restoresRuntimePermissions() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_1_DUP) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has a + * different certificate as the backed up app. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_diffCert_doesNotGrantRuntimePermissions() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_3) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has the + * backed up app's certificate in its signing history. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_midHistoryToRotated_restoresRuntimePermissions() { + install(APP_APK_CERT_2) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has the + * backed up app's certificate as the original certificate in its signing history. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_origToRotated_restoresRuntimePermissions() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the backed up app has the + * restored app's certificate in its signing history. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_rotatedToMidHistory_restoresRuntimePermissions() { + install(APP_APK_CERT_4_HISTORY_1_2_4) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_2) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the backed up app has the + * restored app's certificate in its signing history as its original certificate. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_rotatedToOrig_restoresRuntimePermissions() { + install(APP_APK_CERT_4_HISTORY_1_2_4) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_1) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the backed up app has the same + * certificate as the restored app, but the restored app additionally has signing certificate + * history. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_sameWithHistory_restoresRuntimePermissions() { + install(APP_APK_CERT_4) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the backed up app has the same + * certificate as the restored app, but the backed up app additionally has signing certificate + * history. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_sameWithoutHistory_restoresRuntimePermissions() { + install(APP_APK_CERT_4_HISTORY_1_2_4) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has + * signing history, but the backed up app's certificate is not in this signing history. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_notInBackedUpHistory_doesNotRestoreRuntimePerms() { + install(APP_APK_CERT_4_HISTORY_1_2_4) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_3) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has + * signing history, but the backed up app's certificate is not in this signing history. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_notInRestoredHistory_doesNotRestoreRuntimePerms() { + install(APP_APK_CERT_3) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has + * multiple certificates, and the backed up app also has identical multiple certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_sameMultCerts_restoresRuntimePermissions() { + install(APP_APK_CERT_1_2) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_1_2_DUP) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has + * multiple certificates, and the backed up app do not have identical multiple certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_diffMultCerts_doesNotRestoreRuntimePermissions() { + install(APP_APK_CERT_1_2) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_3_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the app being restored has + * multiple certificates, and the backed up app's certificate is present in th restored app's + * certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_singleToMultiCert_restoresRuntimePerms() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_1_2_3) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the backed up app and the app + * being restored have multiple certificates, and the backed up app's certificates are a subset + * of the restored app's certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_multCertsToSuperset_doesNotRestoreRuntimePerms() { + install(APP_APK_CERT_1_2) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_1_2_3) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of regular runtime permissions, when the backed up app and the app + * being restored have multiple certificates, and the backed up app's certificates are a + * superset of the restored app's certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_multCertsToSubset_doesNotRestoreRuntimePermissions() { + install(APP_APK_CERT_1_2_3) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_1_2) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS)) + } + } + + /** + * Test backup and restore of tri-state permissions, when both foreground and background runtime + * permissions are not granted and the backed up and restored app have compatible certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_fgBgDenied_matchingCerts_restoresFgBgPermissions() { + install(APP_APK_CERT_2) + if (!isBackupSupported) { + return + } + // Make a token change to permission state, to enable to us to determine when restore is + // complete. + grantPermission(APP, WRITE_CONTACTS) + // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order + // to ensure that permissions are backed up. + setFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET) + setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + + // Wait until restore is complete. + assertEquals(PERMISSION_GRANTED, checkPermission(APP, WRITE_CONTACTS)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION)) + assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION)) + } + } + + /** + * Test backup and restore of tri-state permissions, when both foreground and background runtime + * permissions are not granted and the backed up and restored app don't have compatible + * certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_fgBgDenied_notMatchingCerts_doesNotRestorePerms() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + // Make a token change to permission state, to enable to us to determine when restore is + // complete. + grantPermission(APP, WRITE_CONTACTS) + // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order + // to ensure that permissions are backed up. + setFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET) + setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_2) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + + // Wait until restore is complete. + assertEquals(PERMISSION_DENIED, checkPermission(APP, WRITE_CONTACTS)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION)) + assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION)) + } + } + + /** + * Test backup and restore of tri-state permissions, when foreground runtime permission is + * granted and the backed up and restored app have compatible certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_fgGranted_matchingCerts_restoresFgBgPermissions() { + install(APP_APK_CERT_2) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order + // to ensure that permissions are backed up. + setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION)) + assertEquals(AppOpsManager.MODE_FOREGROUND, getAppOp(APP, ACCESS_FINE_LOCATION)) + } + } + + /** + * Test backup and restore of tri-state permissions, when foreground runtime permission is + * granted and the backed up and restored app don't have compatible certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_fgGranted_notMatchingCerts_doesNotRestoreFgBgPerms() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order + // to ensure that permissions are backed up. + setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_2) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION)) + assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION)) + } + } + + /** + * Test backup and restore of tri-state permissions, when foreground and background runtime + * permissions are granted and the backed up and restored app have compatible certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_fgBgGranted_matchingCerts_restoresFgBgPermissions() { + install(APP_APK_CERT_2) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + grantPermission(APP, ACCESS_BACKGROUND_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION)) + assertEquals(AppOpsManager.MODE_ALLOWED, getAppOp(APP, ACCESS_FINE_LOCATION)) + } + } + + /** + * Test backup and restore of tri-state permissions, when foreground and background runtime + * permissions are granted and the backed up and restored app don't have compatible + * certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_fgBgGranted_notMatchingCerts_restoresFgBgPerms() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + grantPermission(APP, ACCESS_BACKGROUND_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_2) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) + assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION)) + assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION)) + } + } + + /** + * Test backup and restore of flags when the backed up app and restored app have compatible + * certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_matchingCerts_restoresFlags() { + install(APP_APK_CERT_2) + if (!isBackupSupported) { + return + } + setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_4_HISTORY_1_2_4) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { assertTrue(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)) } + } + + /** + * Test backup and restore of flags when the backed up app and restored app don't have + * compatible certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_notMatchingCerts_doesNotRestoreFlag() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + install(APP_APK_CERT_2) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + + eventually { assertFalse(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)) } + } + + /** + * Test backup and delayed restore of regular runtime permission, i.e. when an app is installed + * after restore has run, and the backed up app and restored app have compatible certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_appInstalledLater_matchingCerts_restoresCorrectly() { + install(APP_APK_CERT_2) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + install(APP_APK_CERT_4_HISTORY_1_2_4) + + eventually { assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) } + } + + /** + * Test backup and delayed restore of regular runtime permission, i.e. when an app is installed + * after restore has run, and the backed up app and restored app don't have compatible + * certificates. + */ + @Test + @AsbSecurityTest(cveBugId = [184847040]) + fun testRestore_appInstalledLater_notMatchingCerts_doesNotRestore() { + install(APP_APK_CERT_1) + if (!isBackupSupported) { + return + } + grantPermission(APP, ACCESS_FINE_LOCATION) + + backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE) + uninstallIfInstalled(APP) + backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE) + install(APP_APK_CERT_4_HISTORY_1_2_4) + + eventually { assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) } + } + + private fun install(apk: String) { + val output = runShellCommand("pm install -r $apk") + assertEquals("Success", output) + } + + private fun uninstallIfInstalled(packageName: String) { + runShellCommand("pm uninstall $packageName") + } + + private fun setFlag(app: String, permission: String, flag: Int) { + runWithShellPermissionIdentity { + targetContext.packageManager.updatePermissionFlags( + permission, app, flag, flag, targetContext.user) + } + } + + private fun clearFlag(app: String, permission: String, flag: Int) { + runWithShellPermissionIdentity { + targetContext.packageManager.updatePermissionFlags( + permission, app, flag, 0, targetContext.user) + } + } + + private fun isFlagSet(app: String, permission: String, flag: Int): Boolean { + return try { + callWithShellPermissionIdentity<Int> { + targetContext.packageManager.getPermissionFlags(permission, app, targetContext.user) + } and flag == flag + } catch (e: Exception) { + throw RuntimeException(e) + } + } + + private fun checkPermission(app: String, permission: String): Int { + return targetContext.packageManager.checkPermission(permission, app) + } + + private fun getAppOp(app: String, permission: String): Int { + return try { + callWithShellPermissionIdentity { + targetContext + .getSystemService<AppOpsManager>(AppOpsManager::class.java)!! + .unsafeCheckOpRaw( + AppOpsManager.permissionToOp(permission)!!, + targetContext.packageManager.getPackageUid(app, 0), + app) + } + } catch (e: Exception) { + throw RuntimeException(e) + } + } + + companion object { + /** The name of the package of the apps under test */ + private const val APP = "android.security.permissionbackup" + /** The apk of the packages */ + private const val APK_PATH = "/data/local/tmp/cts/security/" + private const val APP_APK_CERT_1 = "${APK_PATH}CtsPermissionBackupAppCert1.apk" + private const val APP_APK_CERT_1_DUP = "${APK_PATH}CtsPermissionBackupAppCert1Dup.apk" + private const val APP_APK_CERT_2 = "${APK_PATH}CtsPermissionBackupAppCert2.apk" + private const val APP_APK_CERT_3 = "${APK_PATH}CtsPermissionBackupAppCert3.apk" + private const val APP_APK_CERT_4 = "${APK_PATH}CtsPermissionBackupAppCert4.apk" + private const val APP_APK_CERT_1_2 = "${APK_PATH}CtsPermissionBackupAppCert12.apk" + private const val APP_APK_CERT_1_2_DUP = "${APK_PATH}CtsPermissionBackupAppCert12Dup.apk" + private const val APP_APK_CERT_1_2_3 = "${APK_PATH}CtsPermissionBackupAppCert123.apk" + private const val APP_APK_CERT_3_4 = "${APK_PATH}CtsPermissionBackupAppCert34.apk" + private const val APP_APK_CERT_4_HISTORY_1_2_4 = + "${APK_PATH}CtsPermissionBackupAppCert4History124.apk" + private const val APP_LOG_TAG = "PermissionBackupApp" + /** The name of the package for backup */ + private const val ANDROID_PACKAGE = "android" + private const val TIMEOUT_MILLIS: Long = 10000 + + /** + * Make sure that a [Runnable] eventually finishes without throwing an [Exception]. + * + * @param r The [Runnable] to run. + */ + fun eventually(r: Runnable) { + val start = System.currentTimeMillis() + while (true) { + try { + r.run() + return + } catch (e: Throwable) { + if (System.currentTimeMillis() - start < TIMEOUT_MILLIS) { + try { + Thread.sleep(100) + } catch (ignored: InterruptedException) { + throw RuntimeException(e) + } + } else { + throw e + } + } + } + } + } +} diff --git a/tests/tests/security/src/android/security/cts/PermissionMemoryFootprintTest.kt b/tests/tests/security/src/android/security/cts/PermissionMemoryFootprintTest.kt new file mode 100644 index 00000000000..c77476021d0 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/PermissionMemoryFootprintTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 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.security.cts + +import android.content.pm.PackageManager +import android.content.pm.PermissionInfo +import android.platform.test.annotations.AsbSecurityTest +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.sts.common.util.StsExtraBusinessLogicTestCase +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class PermissionMemoryFootprintTest : StsExtraBusinessLogicTestCase() { + companion object { + const val MAX_NUM_PERMISSIONS = 32000 + const val PKG_TREE_NAME = "com.android.cts" + val LONG_DESCRIPTION = " ".repeat(MAX_NUM_PERMISSIONS / 10) + val SHORT_DESCRIPTION = " ".repeat(MAX_NUM_PERMISSIONS / 100) + + val permInfo = PermissionInfo().apply { + labelRes = 1 + protectionLevel = PermissionInfo.PROTECTION_NORMAL + } + } + + val packageManager: PackageManager = InstrumentationRegistry.getInstrumentation() + .getTargetContext().packageManager!! + + @Throws(SecurityException::class) + private fun createOrRemovePermissions( + largePerm: Boolean = true, + add: Boolean = true, + numPerms: Int = MAX_NUM_PERMISSIONS, + ): Int { + var numPermsCreated = 0 + for (i in 1..numPerms) { + try { + permInfo.name = "$PKG_TREE_NAME.$i" + permInfo.nonLocalizedDescription = if (largePerm) { + LONG_DESCRIPTION + } else { + SHORT_DESCRIPTION + } + + if (add) { + packageManager.addPermission(permInfo) + } else { + packageManager.removePermission(permInfo.name) + } + } catch (e: SecurityException) { + break + } + numPermsCreated = i + } + return numPermsCreated + } + + @Test + @AsbSecurityTest(cveBugId = [242537498]) + fun checkAppsCreatingPermissionsAreCapped() { + var numCreated = 0 + try { + numCreated = createOrRemovePermissions() + Assert.assertNotEquals("Expected at least one permission", numCreated, 0) + Assert.assertNotEquals(numCreated, MAX_NUM_PERMISSIONS) + } finally { + createOrRemovePermissions(add = false, numPerms = numCreated) + } + } + + @Test + @AsbSecurityTest(cveBugId = [242537498]) + fun checkAppsCantIncreasePermissionSizeAfterCreating() { + var numCreatedShort = 0 + try { + numCreatedShort = createOrRemovePermissions(largePerm = false) + Assert.assertNotEquals("Expected at least one permission", numCreatedShort, 0) + val numCreatedLong = createOrRemovePermissions(numPerms = 1) + Assert.assertEquals("Expected to not be able to create a large permission", + 0, numCreatedLong) + } finally { + createOrRemovePermissions(add = false, numPerms = numCreatedShort) + } + } +} diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java index 307a3e7d01e..9fd10146c1c 100644 --- a/tests/tests/security/src/android/security/cts/StagefrightTest.java +++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java @@ -1377,8 +1377,10 @@ public class StagefrightTest extends StsExtraBusinessLogicTestCase { }; server.start(); String uri = "rtsp://127.0.0.1:8080/cve_2016_3880"; - final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener(new CrashUtils.Config() - .setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT)); + final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener( + new CrashUtils.Config() + .setSignals(CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT) + .appendAbortMessageExcludes("CHECK\\(IsRTSPVersion")); LooperThread t = new LooperThread(new Runnable() { @Override public void run() { @@ -1809,6 +1811,12 @@ public class StagefrightTest extends StsExtraBusinessLogicTestCase { before any existing test methods ***********************************************************/ @Test + @AsbSecurityTest(cveBugId = 235102508) + public void testStagefright_cve_2022_25669() throws Exception { + doStagefrightTest(R.raw.cve_2022_25669); + } + + @Test @AsbSecurityTest(cveBugId = 223209306) public void testStagefright_cve_2022_22085() throws Exception { doStagefrightTest(R.raw.cve_2022_22085); diff --git a/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java index 73474a1b67e..eeed518b713 100644 --- a/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java +++ b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java @@ -76,7 +76,9 @@ public class WallpaperManagerTest extends StsExtraBusinessLogicTestCase { @After public void tearDown() throws Exception { - mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK); + if (mWallpaperManager != null) { + mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK); + } InstrumentationRegistry.getInstrumentation().getUiAutomation() .dropShellPermissionIdentity(); } diff --git a/tests/tests/security/test-cert-1.pk8 b/tests/tests/security/test-cert-1.pk8 Binary files differnew file mode 100644 index 00000000000..f781c3083e5 --- /dev/null +++ b/tests/tests/security/test-cert-1.pk8 diff --git a/tests/tests/security/test-cert-1.x509.pem b/tests/tests/security/test-cert-1.x509.pem new file mode 100644 index 00000000000..06adcfebd3a --- /dev/null +++ b/tests/tests/security/test-cert-1.x509.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBbDCCARGgAwIBAgIJAMoPtk37ZudyMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM +B2VjLXAyNTYwHhcNMTYwMzMxMTQ1ODA2WhcNNDMwODE3MTQ1ODA2WjASMRAwDgYD +VQQDDAdlYy1wMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpl8RPSLLSROQ +gwesMe4roOkTi3hfrGU20U6izpDStL/hlLUM3I4Wn1SnOpke8Pp2MpglvgeMx4J0 +BwPaRLTX66NQME4wHQYDVR0OBBYEFNQTNWi5WzAVizIgceqMQ/9bBczIMB8GA1Ud +IwQYMBaAFNQTNWi5WzAVizIgceqMQ/9bBczIMAwGA1UdEwQFMAMBAf8wCgYIKoZI +zj0EAwIDSQAwRgIhAPUEoIZsrvAp9BcULFy3E1THn/zR1kBhjfyk8Z4W23jWAiEA ++O6kgpeZwGytCMbT0tLsBeBXQVTnR+oP27gELLZVqt0= +-----END CERTIFICATE----- diff --git a/tests/tests/security/test-cert-2.pk8 b/tests/tests/security/test-cert-2.pk8 Binary files differnew file mode 100644 index 00000000000..5e73f27847d --- /dev/null +++ b/tests/tests/security/test-cert-2.pk8 diff --git a/tests/tests/security/test-cert-2.x509.pem b/tests/tests/security/test-cert-2.x509.pem new file mode 100644 index 00000000000..f8e5e65b628 --- /dev/null +++ b/tests/tests/security/test-cert-2.x509.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBbTCCAROgAwIBAgIJAIhVvR3SsrIlMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM +B2VjLXAyNTYwHhcNMTgwNzEzMTc0MTUxWhcNMjgwNzEwMTc0MTUxWjAUMRIwEAYD +VQQDDAllYy1wMjU2XzIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQdTMoEcq2X +7jzs7w2pPWK0UMZ4gzOzbnVTzen3SrXfALu6a6lQ5oRh1wu8JxtiFR2tLeK/YgPN +IHaAHHqdRCLho1AwTjAdBgNVHQ4EFgQUeZHZKwII/ESL9QbU78n/9CjLXl8wHwYD +VR0jBBgwFoAU1BM1aLlbMBWLMiBx6oxD/1sFzMgwDAYDVR0TBAUwAwEB/zAKBggq +hkjOPQQDAgNIADBFAiAnaauxtJ/C9TR5xK6SpmMdq/1SLJrLC7orQ+vrmcYwEQIh +ANJg+x0fF2z5t/pgCYv9JDGfSQWj5f2hAKb+Giqxn/Ce +-----END CERTIFICATE----- diff --git a/tests/tests/security/test-cert-3.pk8 b/tests/tests/security/test-cert-3.pk8 Binary files differnew file mode 100644 index 00000000000..d7309dd3f02 --- /dev/null +++ b/tests/tests/security/test-cert-3.pk8 diff --git a/tests/tests/security/test-cert-3.x509.pem b/tests/tests/security/test-cert-3.x509.pem new file mode 100644 index 00000000000..c028ff7d1e2 --- /dev/null +++ b/tests/tests/security/test-cert-3.x509.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBbjCCARWgAwIBAgIJAIOU9crRaomnMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMM +CWVjLXAyNTZfMjAeFw0xODA3MTQwMDA1MjZaFw0yODA3MTEwMDA1MjZaMBQxEjAQ +BgNVBAMMCWVjLXAyNTZfMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMeYkMO +nbb8WSjZdfxOR0GbrPyy4HyJKZ5s1+NE3SGt/TCNWMtJoaKj/srM7qSGIGnzC+Fk +O8wlUEDYCJ37N0OjUDBOMB0GA1UdDgQWBBRvjQgosT769Xf8hrDpn6PlS8vP8DAf +BgNVHSMEGDAWgBR5kdkrAgj8RIv1BtTvyf/0KMteXzAMBgNVHRMEBTADAQH/MAoG +CCqGSM49BAMCA0cAMEQCICVr2qJ4TCc+TMKRpZWkZ3ne6d6QRNyferggMJVn35/p +AiAaStjGmJG1qMR0NP6VQO0fSXm1+tNIPz+gTVZ3NVpXng== +-----END CERTIFICATE----- diff --git a/tests/tests/security/test-cert-4.pk8 b/tests/tests/security/test-cert-4.pk8 Binary files differnew file mode 100644 index 00000000000..3675d50c54f --- /dev/null +++ b/tests/tests/security/test-cert-4.pk8 diff --git a/tests/tests/security/test-cert-4.x509.pem b/tests/tests/security/test-cert-4.x509.pem new file mode 100644 index 00000000000..4060400cb6c --- /dev/null +++ b/tests/tests/security/test-cert-4.x509.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBezCCASCgAwIBAgIUbIy4qBhDPB5kMfsW+zrg+1rWCqcwCgYIKoZIzj0EAwIw +FDESMBAGA1UEAwwJZWMtcDI1Nl8zMB4XDTIwMDUxMzE5MTUyOFoXDTMwMDUxMTE5 +MTUyOFowFDESMBAGA1UEAwwJZWMtcDI1Nl80MFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAE20pgAx55rUnLdZAH1oVdRGm5HIurBlQ08vupca3n5NGVmaD2e15wjP2n +VD5WMMN2nTfgk2QNfHaKFRRM0OXc9KNQME4wHQYDVR0OBBYEFG54lwMyVUM2tu6J +JOqnAjDjk/Z4MB8GA1UdIwQYMBaAFG+NCCixPvr1d/yGsOmfo+VLy8/wMAwGA1Ud +EwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAM54bnnsdUdEYILpyvkQYU/4B1j5 +gZ+w8UhpUGer4PzUAiEApIgeMy3ewhFq0rWc+JHQ8zH/fifne3xiBseYjZtTkzA= +-----END CERTIFICATE----- diff --git a/tests/tests/security/test-cert-with-1-2-4-in-rotation-history b/tests/tests/security/test-cert-with-1-2-4-in-rotation-history Binary files differnew file mode 100644 index 00000000000..7326e46f85b --- /dev/null +++ b/tests/tests/security/test-cert-with-1-2-4-in-rotation-history diff --git a/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml b/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml new file mode 100644 index 00000000000..2b75d8c846a --- /dev/null +++ b/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 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.security.permissionbackup" > + + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> + <uses-permission android:name="android.permission.READ_CONTACTS"/> + <uses-permission android:name="android.permission.WRITE_CONTACTS"/> + + <application + android:label="Android Permission Backup CTS App"> + <uses-library android:name="android.test.runner" /> + </application> +</manifest> diff --git a/tests/tests/webkit/Android.bp b/tests/tests/webkit/Android.bp index d8175dd4423..a046dae6fe3 100644 --- a/tests/tests/webkit/Android.bp +++ b/tests/tests/webkit/Android.bp @@ -25,6 +25,7 @@ android_test { "android.test.base", ], static_libs: [ + "androidx.test.core", "compatibility-device-util-axt", "ctsdeviceutillegacy-axt", "ctstestserver", diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java index abe44ea1155..7ca3f468353 100755 --- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java +++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java @@ -19,6 +19,7 @@ package android.webkit.cts; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; +import static org.junit.Assert.*; import android.content.ContentResolver; import android.content.Context; @@ -49,18 +50,13 @@ import android.print.PrintDocumentAdapter; import android.print.PrintDocumentAdapter.LayoutResultCallback; import android.print.PrintDocumentAdapter.WriteResultCallback; import android.print.PrintDocumentInfo; -import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; -import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.View; import android.view.ViewGroup; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextSelection; -import android.webkit.ConsoleMessage; import android.webkit.CookieSyncManager; import android.webkit.DownloadListener; import android.webkit.JavascriptInterface; @@ -81,47 +77,53 @@ import android.webkit.cts.WebViewSyncLoader.WaitForLoadedClient; import android.webkit.cts.WebViewSyncLoader.WaitForProgressClient; import android.widget.LinearLayout; +import androidx.test.InstrumentationRegistry; +import androidx.test.core.app.ActivityScenario; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.FlakyTest; import com.android.compatibility.common.util.NullWebViewUtils; import com.android.compatibility.common.util.PollingCheck; + import com.google.common.util.concurrent.SettableFuture; -import java.io.ByteArrayInputStream; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpRequest; +import org.apache.http.util.EncodingUtils; +import org.apache.http.util.EntityUtils; +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; - import java.net.MalformedURLException; import java.net.URL; - import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; - -import java.util.Collections; -import java.util.Date; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import java.util.concurrent.FutureTask; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpRequest; -import org.apache.http.util.EncodingUtils; -import org.apache.http.util.EntityUtils; +import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; @AppModeFull -public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> { +@RunWith(AndroidJUnit4.class) +public class WebViewTest { private static final int INITIAL_PROGRESS = 100; private static final String X_REQUESTED_WITH = "X-Requested-With"; private static final String PRINTER_TEST_FILE = "print.pdf"; @@ -132,59 +134,62 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi private static final String SIMPLE_HTML = "<html><body>simple html</body></html>"; /** - * This is the minimum number of milliseconds to wait for scrolling to - * start. If no scrolling has started before this timeout then it is - * assumed that no scrolling will happen. + * This is the minimum number of milliseconds to wait for scrolling to start. If no scrolling + * has started before this timeout then it is assumed that no scrolling will happen. */ private static final long MIN_SCROLL_WAIT_MS = 1000; /** - * This is the minimum number of milliseconds to wait for findAll to - * find all the matches. If matches are not found, the Listener would - * call findAll again until it times out. + * This is the minimum number of milliseconds to wait for findAll to find all the matches. If + * matches are not found, the Listener would call findAll again until it times out. */ private static final long MIN_FIND_WAIT_MS = 3000; /** - * Once scrolling has started, this is the interval that scrolling - * is checked to see if there is a change. If no scrolling change - * has happened in the given time then it is assumed that scrolling - * has stopped. + * Once scrolling has started, this is the interval that scrolling is checked to see if there is + * a change. If no scrolling change has happened in the given time then it is assumed that + * scrolling has stopped. */ private static final long SCROLL_WAIT_INTERVAL_MS = 200; + @Rule + public ActivityScenarioRule mActivityScenarioRule = + new ActivityScenarioRule(WebViewCtsActivity.class); + + private ActivityScenario mScenario; + private WebViewCtsActivity mActivity; private WebView mWebView; private CtsTestServer mWebServer; private WebViewOnUiThread mOnUiThread; private WebIconDatabase mIconDb; - public WebViewTest() { - super("com.android.cts.webkit", WebViewCtsActivity.class); - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - final WebViewCtsActivity activity = getActivity(); - mWebView = activity.getWebView(); + @Before + public void setUp() throws Exception { + mScenario = mActivityScenarioRule.getScenario(); + mScenario.onActivity( + activity -> { + mActivity = (WebViewCtsActivity) activity; + mWebView = mActivity.getWebView(); + }); if (mWebView != null) { new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override - protected boolean check() { - return activity.hasWindowFocus(); + protected boolean check() { + return mActivity.hasWindowFocus(); } }.run(); - File f = activity.getFileStreamPath("snapshot"); + File f = mActivity.getFileStreamPath("snapshot"); if (f.exists()) { f.delete(); } mOnUiThread = new WebViewOnUiThread(mWebView); } + Assume.assumeTrue("WebView is not available", NullWebViewUtils.isWebViewAvailable()); } - @Override - protected void tearDown() throws Exception { + @After + public void cleanup() throws Exception { if (mOnUiThread != null) { mOnUiThread.cleanUp(); } @@ -196,171 +201,176 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mIconDb.close(); mIconDb = null; } - super.tearDown(); + mActivity = null; } private void startWebServer(boolean secure) throws Exception { assertNull(mWebServer); - mWebServer = new CtsTestServer(getActivity(), secure); + mWebServer = new CtsTestServer(mActivity, secure); } private void stopWebServer() throws Exception { assertNotNull(mWebServer); ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); - ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy) - .permitNetwork() - .build(); + ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy).permitNetwork().build(); StrictMode.setThreadPolicy(tmpPolicy); mWebServer.shutdown(); mWebServer = null; StrictMode.setThreadPolicy(oldPolicy); } - @UiThreadTest + @Test public void testConstructor() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - WebView webView = new WebView(getActivity()); - webView.destroy(); - webView = new WebView(getActivity(), null); - webView.destroy(); - webView = new WebView(getActivity(), null, 0); - webView.destroy(); + WebkitUtils.onMainThreadSync( + () -> { + WebView webView = new WebView(mActivity); + webView.destroy(); + webView = new WebView(mActivity, null); + webView.destroy(); + webView = new WebView(mActivity, null, 0); + webView.destroy(); + }); } - @UiThreadTest + @Test public void testCreatingWebViewWithDeviceEncrpytionFails() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - Context deviceEncryptedContext = getActivity().createDeviceProtectedStorageContext(); - try { - new WebView(deviceEncryptedContext); - fail("WebView should have thrown exception when creating with a device " + - "protected storage context"); - } catch (IllegalArgumentException e) {} + WebkitUtils.onMainThreadSync( + () -> { + Context deviceEncryptedContext = + mActivity.createDeviceProtectedStorageContext(); + try { + new WebView(deviceEncryptedContext); + fail( + "WebView should have thrown exception when creating with a device " + + "protected storage context"); + } catch (IllegalArgumentException e) { + } + }); } - @UiThreadTest + @Test public void testCreatingWebViewWithMultipleEncryptionContext() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - // Credential encrpytion is the default. Create one here for the sake of clarity. - Context credentialEncryptedContext = getActivity().createCredentialProtectedStorageContext(); - Context deviceEncryptedContext = getActivity().createDeviceProtectedStorageContext(); + WebkitUtils.onMainThreadSync( + () -> { + // Credential encryption is the default. Create one here for the sake of + // clarity. + Context credentialEncryptedContext = + mActivity.createCredentialProtectedStorageContext(); + Context deviceEncryptedContext = + mActivity.createDeviceProtectedStorageContext(); + + // No exception should be thrown with credential encryption context. + WebView webView = new WebView(credentialEncryptedContext); + webView.destroy(); - // No exception should be thrown with credential encryption context. - WebView webView = new WebView(credentialEncryptedContext); - webView.destroy(); - - try { - new WebView(deviceEncryptedContext); - fail("WebView should have thrown exception when creating with a device " + - "protected storage context"); - } catch (IllegalArgumentException e) {} + try { + new WebView(deviceEncryptedContext); + fail( + "WebView should have thrown exception when creating with a device " + + "protected storage context"); + } catch (IllegalArgumentException e) { + } + }); } - @UiThreadTest + @Test public void testCreatingWebViewCreatesCookieSyncManager() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - WebView webView = new WebView(getActivity()); - assertNotNull(CookieSyncManager.getInstance()); - webView.destroy(); + WebkitUtils.onMainThreadSync( + () -> { + WebView webView = new WebView(mActivity); + assertNotNull(CookieSyncManager.getInstance()); + webView.destroy(); + }); } + @Test // Static methods should be safe to call on non-UI threads public void testFindAddress() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - /* * Info about USPS * http://en.wikipedia.org/wiki/Postal_address#United_States * http://www.usps.com/ */ // full address - assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826", + assertEquals( + "455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826", WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826")); // Zipcode is optional. - assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA", + assertEquals( + "455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA", WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA")); // not an address assertNull(WebView.findAddress("This is not an address: no town, no state, no zip.")); // would be an address, except for numbers that are not ASCII - assertNull(WebView.findAddress( - "80\uD835\uDFEF \uD835\uDFEF\uD835\uDFEFth Avenue Sunnyvale, CA 94089")); + assertNull( + WebView.findAddress( + "80\uD835\uDFEF \uD835\uDFEF\uD835\uDFEFth Avenue Sunnyvale, CA 94089")); } - @UiThreadTest + @Test public void testScrollBarOverlay() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - // These functions have no effect; just verify they don't crash - mWebView.setHorizontalScrollbarOverlay(true); - mWebView.setVerticalScrollbarOverlay(false); - - assertTrue(mWebView.overlayHorizontalScrollbar()); - assertFalse(mWebView.overlayVerticalScrollbar()); + WebkitUtils.onMainThreadSync( + () -> { + // These functions have no effect; just verify they don't crash + mWebView.setHorizontalScrollbarOverlay(true); + mWebView.setVerticalScrollbarOverlay(false); + + assertTrue(mWebView.overlayHorizontalScrollbar()); + assertFalse(mWebView.overlayVerticalScrollbar()); + }); } + @Test @Presubmit - @UiThreadTest public void testLoadUrl() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - assertNull(mWebView.getUrl()); - assertNull(mWebView.getOriginalUrl()); - assertEquals(INITIAL_PROGRESS, mWebView.getProgress()); - startWebServer(false); - String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); - mOnUiThread.loadUrlAndWaitForCompletion(url); - assertEquals(100, mWebView.getProgress()); - assertEquals(url, mWebView.getUrl()); - assertEquals(url, mWebView.getOriginalUrl()); - assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle()); - - // verify that the request also includes X-Requested-With header - HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); - Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH); - assertEquals(1, matchingHeaders.length); - Header header = matchingHeaders[0]; - assertEquals(mWebView.getContext().getApplicationInfo().packageName, header.getValue()); + WebkitUtils.onMainThreadSync( + () -> { + assertNull(mWebView.getUrl()); + assertNull(mWebView.getOriginalUrl()); + assertEquals(INITIAL_PROGRESS, mWebView.getProgress()); + + String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); + mOnUiThread.loadUrlAndWaitForCompletion(url); + assertEquals(100, mWebView.getProgress()); + assertEquals(url, mWebView.getUrl()); + assertEquals(url, mWebView.getOriginalUrl()); + assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle()); + + // verify that the request also includes X-Requested-With header + HttpRequest request = + mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); + Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH); + assertEquals(1, matchingHeaders.length); + + Header header = matchingHeaders[0]; + assertEquals( + mWebView.getContext().getApplicationInfo().packageName, + header.getValue()); + }); } - @UiThreadTest + @Test public void testPostUrlWithNonNetworkUrl() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final String nonNetworkUrl = "file:///android_asset/" + TestHtmlConstants.HELLO_WORLD_URL; mOnUiThread.postUrlAndWaitForCompletion(nonNetworkUrl, new byte[1]); - assertEquals("Non-network URL should have loaded", TestHtmlConstants.HELLO_WORLD_TITLE, - mWebView.getTitle()); + WebkitUtils.onMainThreadSync( + () -> { + assertEquals( + "Non-network URL should have loaded", + TestHtmlConstants.HELLO_WORLD_TITLE, + mWebView.getTitle()); + }); } - @UiThreadTest + @Test public void testPostUrlWithNetworkUrl() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } startWebServer(false); + final String networkUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); final String postDataString = "username=my_username&password=my_password"; final byte[] postData = EncodingUtils.getBytes(postDataString, "BASE64"); @@ -368,83 +378,84 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mOnUiThread.postUrlAndWaitForCompletion(networkUrl, postData); HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); - assertEquals("The last request should be POST", request.getRequestLine().getMethod(), - "POST"); + assertEquals( + "The last request should be POST", request.getRequestLine().getMethod(), "POST"); - assertTrue("The last request should have a request body", + assertTrue( + "The last request should have a request body", request instanceof HttpEntityEnclosingRequest); HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); String entityString = EntityUtils.toString(entity); assertEquals(entityString, postDataString); } - @UiThreadTest + @Test public void testLoadUrlDoesNotStripParamsWhenLoadingContentUrls() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - Uri.Builder uriBuilder = new Uri.Builder().scheme( - ContentResolver.SCHEME_CONTENT).authority(MockContentProvider.AUTHORITY); - uriBuilder.appendPath("foo.html").appendQueryParameter("param","bar"); - String url = uriBuilder.build().toString(); - mOnUiThread.loadUrlAndWaitForCompletion(url); - // verify the parameter is not stripped. - Uri uri = Uri.parse(mWebView.getTitle()); - assertEquals("bar", uri.getQueryParameter("param")); + WebkitUtils.onMainThreadSync( + () -> { + Uri.Builder uriBuilder = + new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(MockContentProvider.AUTHORITY); + uriBuilder.appendPath("foo.html").appendQueryParameter("param", "bar"); + String url = uriBuilder.build().toString(); + mOnUiThread.loadUrlAndWaitForCompletion(url); + // verify the parameter is not stripped. + Uri uri = Uri.parse(mWebView.getTitle()); + assertEquals("bar", uri.getQueryParameter("param")); + }); } - @UiThreadTest + @Test public void testAppInjectedXRequestedWithHeaderIsNotOverwritten() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - startWebServer(false); - String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); - HashMap<String, String> map = new HashMap<String, String>(); - final String requester = "foo"; - map.put(X_REQUESTED_WITH, requester); - mOnUiThread.loadUrlAndWaitForCompletion(url, map); - // verify that the request also includes X-Requested-With header - // but is not overwritten by the webview - HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); - Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH); - assertEquals(1, matchingHeaders.length); - - Header header = matchingHeaders[0]; - assertEquals(requester, header.getValue()); + WebkitUtils.onMainThreadSync( + () -> { + String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); + HashMap<String, String> map = new HashMap<String, String>(); + final String requester = "foo"; + map.put(X_REQUESTED_WITH, requester); + mOnUiThread.loadUrlAndWaitForCompletion(url, map); + + // verify that the request also includes X-Requested-With header + // but is not overwritten by the webview + HttpRequest request = + mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); + Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH); + assertEquals(1, matchingHeaders.length); + + Header header = matchingHeaders[0]; + assertEquals(requester, header.getValue()); + }); } - @UiThreadTest + @Test public void testAppCanInjectHeadersViaImmutableMap() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - startWebServer(false); - String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); - HashMap<String, String> map = new HashMap<String, String>(); - final String requester = "foo"; - map.put(X_REQUESTED_WITH, requester); - mOnUiThread.loadUrlAndWaitForCompletion(url, Collections.unmodifiableMap(map)); - - // verify that the request also includes X-Requested-With header - // but is not overwritten by the webview - HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); - Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH); - assertEquals(1, matchingHeaders.length); - Header header = matchingHeaders[0]; - assertEquals(requester, header.getValue()); + WebkitUtils.onMainThreadSync( + () -> { + String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); + HashMap<String, String> map = new HashMap<String, String>(); + final String requester = "foo"; + map.put(X_REQUESTED_WITH, requester); + mOnUiThread.loadUrlAndWaitForCompletion(url, Collections.unmodifiableMap(map)); + + // verify that the request also includes X-Requested-With header + // but is not overwritten by the webview + HttpRequest request = + mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); + Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH); + assertEquals(1, matchingHeaders.length); + + Header header = matchingHeaders[0]; + assertEquals(requester, header.getValue()); + }); } + @Test public void testCanInjectHeaders() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - final String X_FOO = "X-foo"; final String X_FOO_VALUE = "test"; @@ -458,7 +469,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mOnUiThread.loadUrlAndWaitForCompletion(url, map); HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL); - for (Map.Entry<String,String> value : map.entrySet()) { + for (Map.Entry<String, String> value : map.entrySet()) { String header = value.getKey(); Header[] matchingHeaders = request.getHeaders(header); assertEquals("header " + header + " not found", 1, matchingHeaders.length); @@ -466,49 +477,47 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } } + @Test @SuppressWarnings("deprecation") - @UiThreadTest public void testGetVisibleTitleHeight() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - startWebServer(false); - String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); - mOnUiThread.loadUrlAndWaitForCompletion(url); - assertEquals(0, mWebView.getVisibleTitleHeight()); + + WebkitUtils.onMainThreadSync( + () -> { + String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); + mOnUiThread.loadUrlAndWaitForCompletion(url); + assertEquals(0, mWebView.getVisibleTitleHeight()); + }); } - @UiThreadTest + @Test public void testGetOriginalUrl() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - startWebServer(false); - final String finalUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); - final String redirectUrl = - mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); - - assertNull(mWebView.getUrl()); - assertNull(mWebView.getOriginalUrl()); - - // By default, WebView sends an intent to ask the system to - // handle loading a new URL. We set a WebViewClient as - // WebViewClient.shouldOverrideUrlLoading() returns false, so - // the WebView will load the new URL. - mWebView.setWebViewClient(new WaitForLoadedClient(mOnUiThread)); - mOnUiThread.loadUrlAndWaitForCompletion(redirectUrl); - assertEquals(finalUrl, mWebView.getUrl()); - assertEquals(redirectUrl, mWebView.getOriginalUrl()); + WebkitUtils.onMainThreadSync( + () -> { + final String finalUrl = + mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); + final String redirectUrl = + mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); + + assertNull(mWebView.getUrl()); + assertNull(mWebView.getOriginalUrl()); + + // By default, WebView sends an intent to ask the system to + // handle loading a new URL. We set a WebViewClient as + // WebViewClient.shouldOverrideUrlLoading() returns false, so + // the WebView will load the new URL. + mWebView.setWebViewClient(new WaitForLoadedClient(mOnUiThread)); + mOnUiThread.loadUrlAndWaitForCompletion(redirectUrl); + + assertEquals(finalUrl, mWebView.getUrl()); + assertEquals(redirectUrl, mWebView.getOriginalUrl()); + }); } + @Test public void testStopLoading() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - assertEquals(INITIAL_PROGRESS, mOnUiThread.getProgress()); startWebServer(false); @@ -522,6 +531,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mPageLoaded = true; notify(); } + public synchronized boolean getPageLoaded() { return mPageLoaded; } @@ -542,61 +552,58 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertFalse(jsInterface.getPageLoaded()); } - @UiThreadTest + @Test public void testGoBackAndForward() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - assertGoBackOrForwardBySteps(false, -1); - assertGoBackOrForwardBySteps(false, 1); - startWebServer(false); - String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1); - String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); - String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3); - - mOnUiThread.loadUrlAndWaitForCompletion(url1); - pollingCheckWebBackForwardList(url1, 0, 1); - assertGoBackOrForwardBySteps(false, -1); - assertGoBackOrForwardBySteps(false, 1); - - mOnUiThread.loadUrlAndWaitForCompletion(url2); - pollingCheckWebBackForwardList(url2, 1, 2); - assertGoBackOrForwardBySteps(true, -1); - assertGoBackOrForwardBySteps(false, 1); - mOnUiThread.loadUrlAndWaitForCompletion(url3); - pollingCheckWebBackForwardList(url3, 2, 3); - assertGoBackOrForwardBySteps(true, -2); - assertGoBackOrForwardBySteps(false, 1); - - mWebView.goBack(); - pollingCheckWebBackForwardList(url2, 1, 3); - assertGoBackOrForwardBySteps(true, -1); - assertGoBackOrForwardBySteps(true, 1); - - mWebView.goForward(); - pollingCheckWebBackForwardList(url3, 2, 3); - assertGoBackOrForwardBySteps(true, -2); - assertGoBackOrForwardBySteps(false, 1); - - mWebView.goBackOrForward(-2); - pollingCheckWebBackForwardList(url1, 0, 3); - assertGoBackOrForwardBySteps(false, -1); - assertGoBackOrForwardBySteps(true, 2); - - mWebView.goBackOrForward(2); - pollingCheckWebBackForwardList(url3, 2, 3); - assertGoBackOrForwardBySteps(true, -2); - assertGoBackOrForwardBySteps(false, 1); + WebkitUtils.onMainThreadSync( + () -> { + assertGoBackOrForwardBySteps(false, -1); + assertGoBackOrForwardBySteps(false, 1); + + String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1); + String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); + String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3); + + mOnUiThread.loadUrlAndWaitForCompletion(url1); + pollingCheckWebBackForwardList(url1, 0, 1); + assertGoBackOrForwardBySteps(false, -1); + assertGoBackOrForwardBySteps(false, 1); + + mOnUiThread.loadUrlAndWaitForCompletion(url2); + pollingCheckWebBackForwardList(url2, 1, 2); + assertGoBackOrForwardBySteps(true, -1); + assertGoBackOrForwardBySteps(false, 1); + + mOnUiThread.loadUrlAndWaitForCompletion(url3); + pollingCheckWebBackForwardList(url3, 2, 3); + assertGoBackOrForwardBySteps(true, -2); + assertGoBackOrForwardBySteps(false, 1); + + mWebView.goBack(); + pollingCheckWebBackForwardList(url2, 1, 3); + assertGoBackOrForwardBySteps(true, -1); + assertGoBackOrForwardBySteps(true, 1); + + mWebView.goForward(); + pollingCheckWebBackForwardList(url3, 2, 3); + assertGoBackOrForwardBySteps(true, -2); + assertGoBackOrForwardBySteps(false, 1); + + mWebView.goBackOrForward(-2); + pollingCheckWebBackForwardList(url1, 0, 3); + assertGoBackOrForwardBySteps(false, -1); + assertGoBackOrForwardBySteps(true, 2); + + mWebView.goBackOrForward(2); + pollingCheckWebBackForwardList(url3, 2, 3); + assertGoBackOrForwardBySteps(true, -2); + assertGoBackOrForwardBySteps(false, 1); + }); } + @Test public void testAddJavascriptInterface() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.getSettings().setJavaScriptEnabled(true); mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); @@ -641,77 +648,66 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi // Verify that only methods annotated with @JavascriptInterface are exposed // on the JavaScript interface object. - assertEquals("\"function\"", + assertEquals( + "\"function\"", mOnUiThread.evaluateJavascriptSync("typeof interface.provideResult")); - assertEquals("\"undefined\"", + assertEquals( + "\"undefined\"", mOnUiThread.evaluateJavascriptSync("typeof interface.wasProvideResultCalled")); - assertEquals("\"undefined\"", - mOnUiThread.evaluateJavascriptSync("typeof interface.getClass")); + assertEquals( + "\"undefined\"", mOnUiThread.evaluateJavascriptSync("typeof interface.getClass")); } + @Test public void testAddJavascriptInterfaceNullObject() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.getSettings().setJavaScriptEnabled(true); - String setTitleToPropertyTypeHtml = "<html><head></head>" + - "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>"; + String setTitleToPropertyTypeHtml = + "<html><head></head><body onload=\"document.title = typeof" + + " window.injectedObject;\"></body></html>"; // Test that the property is initially undefined. - mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, - "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null); assertEquals("undefined", mOnUiThread.getTitle()); // Test that adding a null object has no effect. mOnUiThread.addJavascriptInterface(null, "injectedObject"); - mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, - "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null); assertEquals("undefined", mOnUiThread.getTitle()); // Test that adding an object gives an object type. final Object obj = new Object(); mOnUiThread.addJavascriptInterface(obj, "injectedObject"); - mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, - "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null); assertEquals("object", mOnUiThread.getTitle()); // Test that trying to replace with a null object has no effect. mOnUiThread.addJavascriptInterface(null, "injectedObject"); - mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, - "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null); assertEquals("object", mOnUiThread.getTitle()); } + @Test public void testRemoveJavascriptInterface() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.getSettings().setJavaScriptEnabled(true); - String setTitleToPropertyTypeHtml = "<html><head></head>" + - "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>"; + String setTitleToPropertyTypeHtml = + "<html><head></head><body onload=\"document.title = typeof" + + " window.injectedObject;\"></body></html>"; // Test that adding an object gives an object type. mOnUiThread.addJavascriptInterface(new Object(), "injectedObject"); - mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, - "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null); assertEquals("object", mOnUiThread.getTitle()); // Test that reloading the page after removing the object leaves the property undefined. mOnUiThread.removeJavascriptInterface("injectedObject"); - mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, - "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml, "text/html", null); assertEquals("undefined", mOnUiThread.getTitle()); } + @Test public void testUseRemovedJavascriptInterface() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - class RemovedObject { @Override @JavascriptInterface @@ -735,6 +731,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mIsResultAvailable = true; notify(); } + public synchronized String getResult() { while (!mIsResultAvailable) { try { @@ -752,30 +749,31 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mOnUiThread.getSettings().setJavaScriptEnabled(true); mOnUiThread.addJavascriptInterface(new RemovedObject(), "removedObject"); mOnUiThread.addJavascriptInterface(resultObject, "resultObject"); - mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" + - "<body onload=\"window.removedObject.remove();" + - "resultObject.setResult(removedObject.toString());\"></body></html>", - "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><head></head>" + + "<body onload=\"window.removedObject.remove();" + + "resultObject.setResult(removedObject.toString());\"></body></html>", + "text/html", + null); assertEquals("removedObject", resultObject.getResult()); } + @Test public void testAddJavascriptInterfaceExceptions() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } WebSettings settings = mOnUiThread.getSettings(); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); - final AtomicBoolean mJsInterfaceWasCalled = new AtomicBoolean(false) { - @JavascriptInterface - public synchronized void call() { - set(true); - // The main purpose of this test is to ensure an exception here does not - // crash the implementation. - throw new RuntimeException("Javascript Interface exception"); - } - }; + final AtomicBoolean mJsInterfaceWasCalled = + new AtomicBoolean(false) { + @JavascriptInterface + public synchronized void call() { + set(true); + // The main purpose of this test is to ensure an exception here does not + // crash the implementation. + throw new RuntimeException("Javascript Interface exception"); + } + }; mOnUiThread.addJavascriptInterface(mJsInterfaceWasCalled, "interface"); @@ -783,16 +781,15 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertFalse(mJsInterfaceWasCalled.get()); - assertEquals("\"pass\"", mOnUiThread.evaluateJavascriptSync( - "try {interface.call(); 'fail'; } catch (exception) { 'pass'; } ")); + assertEquals( + "\"pass\"", + mOnUiThread.evaluateJavascriptSync( + "try {interface.call(); 'fail'; } catch (exception) { 'pass'; } ")); assertTrue(mJsInterfaceWasCalled.get()); } + @Test public void testJavascriptInterfaceCustomPropertiesClearedOnReload() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.getSettings().setJavaScriptEnabled(true); mOnUiThread.addJavascriptInterface(new Object(), "interface"); @@ -807,12 +804,9 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertEquals("false", mOnUiThread.evaluateJavascriptSync("'custom_property' in interface")); } + @Test @FlakyTest(bugId = 171702662) public void testJavascriptInterfaceForClientPopup() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.getSettings().setJavaScriptEnabled(true); mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); mOnUiThread.getSettings().setSupportMultipleWindows(true); @@ -831,31 +825,40 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi childOnUiThread.addJavascriptInterface(obj, "interface"); final SettableFuture<Void> onCreateWindowFuture = SettableFuture.create(); - mOnUiThread.setWebChromeClient(new WebViewSyncLoader.WaitForProgressClient(mOnUiThread) { - @Override - public boolean onCreateWindow( - WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { - getActivity().addContentView(childWebView, new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; - transport.setWebView(childWebView); - resultMsg.sendToTarget(); - onCreateWindowFuture.set(null); - return true; - } - }); + mOnUiThread.setWebChromeClient( + new WebViewSyncLoader.WaitForProgressClient(mOnUiThread) { + @Override + public boolean onCreateWindow( + WebView view, + boolean isDialog, + boolean isUserGesture, + Message resultMsg) { + mActivity.addContentView( + childWebView, + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + WebView.WebViewTransport transport = + (WebView.WebViewTransport) resultMsg.obj; + transport.setWebView(childWebView); + resultMsg.sendToTarget(); + onCreateWindowFuture.set(null); + return true; + } + }); startWebServer(false); - mOnUiThread.loadUrlAndWaitForCompletion(mWebServer. - getAssetUrl(TestHtmlConstants.POPUP_URL)); + mOnUiThread.loadUrlAndWaitForCompletion( + mWebServer.getAssetUrl(TestHtmlConstants.POPUP_URL)); WebkitUtils.waitForFuture(onCreateWindowFuture); childOnUiThread.loadUrlAndWaitForCompletion("about:blank"); assertEquals("true", childOnUiThread.evaluateJavascriptSync("'interface' in window")); - assertEquals("The injected object should be functional", "42", + assertEquals( + "The injected object should be functional", + "42", childOnUiThread.evaluateJavascriptSync("interface.test()")); } @@ -871,17 +874,17 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } } - private Picture waitForPictureToHaveColor(int color, - final TestPictureListener listener) throws Throwable { + private Picture waitForPictureToHaveColor(int color, final TestPictureListener listener) + throws Throwable { final int MAX_ON_NEW_PICTURE_ITERATIONS = 5; final AtomicReference<Picture> pictureRef = new AtomicReference<Picture>(); for (int i = 0; i < MAX_ON_NEW_PICTURE_ITERATIONS; i++) { final int oldCallCount = listener.callCount; - WebkitUtils.onMainThreadSync(() -> { - pictureRef.set(mWebView.capturePicture()); - }); - if (isPictureFilledWithColor(pictureRef.get(), color)) - break; + WebkitUtils.onMainThreadSync( + () -> { + pictureRef.set(mWebView.capturePicture()); + }); + if (isPictureFilledWithColor(pictureRef.get(), color)) break; new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { @@ -892,10 +895,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi return pictureRef.get(); } + @Test public void testCapturePicture() throws Exception, Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final TestPictureListener listener = new TestPictureListener(); startWebServer(false); @@ -906,20 +907,20 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi // The default background color is white. Picture oldPicture = waitForPictureToHaveColor(Color.WHITE, listener); - WebkitUtils.onMainThread(() -> { - mWebView.setBackgroundColor(Color.CYAN); - }); + WebkitUtils.onMainThread( + () -> { + mWebView.setBackgroundColor(Color.CYAN); + }); mOnUiThread.reloadAndWaitForCompletion(); waitForPictureToHaveColor(Color.CYAN, listener); - assertTrue("The content of the previously captured picture should not update automatically", + assertTrue( + "The content of the previously captured picture should not update automatically", isPictureFilledWithColor(oldPicture, Color.WHITE)); } + @Test public void testSetPictureListener() throws Exception, Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final class MyPictureListener implements PictureListener { public int callCount; public WebView webView; @@ -961,149 +962,145 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); } - @UiThreadTest + @Test public void testAccessHttpAuthUsernamePassword() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - try { - WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword(); - - String host = "http://localhost:8080"; - String realm = "testrealm"; - String userName = "user"; - String password = "password"; - - String[] result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNull(result); - - mWebView.setHttpAuthUsernamePassword(host, realm, userName, password); - result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(userName, result[0]); - assertEquals(password, result[1]); - - String newPassword = "newpassword"; - mWebView.setHttpAuthUsernamePassword(host, realm, userName, newPassword); - result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(userName, result[0]); - assertEquals(newPassword, result[1]); - - String newUserName = "newuser"; - mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword); - result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(newUserName, result[0]); - assertEquals(newPassword, result[1]); - - // the user is set to null, can not change any thing in the future - mWebView.setHttpAuthUsernamePassword(host, realm, null, password); - result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertNull(result[0]); - assertEquals(password, result[1]); - - mWebView.setHttpAuthUsernamePassword(host, realm, userName, null); - result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(userName, result[0]); - assertNull(result[1]); - - mWebView.setHttpAuthUsernamePassword(host, realm, null, null); - result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertNull(result[0]); - assertNull(result[1]); - - mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword); - result = mWebView.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(newUserName, result[0]); - assertEquals(newPassword, result[1]); - } finally { - WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword(); - } + WebkitUtils.onMainThreadSync( + () -> { + try { + WebViewDatabase.getInstance(mActivity).clearHttpAuthUsernamePassword(); + + String host = "http://localhost:8080"; + String realm = "testrealm"; + String userName = "user"; + String password = "password"; + + String[] result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNull(result); + + mWebView.setHttpAuthUsernamePassword(host, realm, userName, password); + result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(userName, result[0]); + assertEquals(password, result[1]); + + String newPassword = "newpassword"; + mWebView.setHttpAuthUsernamePassword(host, realm, userName, newPassword); + result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(userName, result[0]); + assertEquals(newPassword, result[1]); + + String newUserName = "newuser"; + mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword); + result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(newUserName, result[0]); + assertEquals(newPassword, result[1]); + + // the user is set to null, can not change any thing in the future + mWebView.setHttpAuthUsernamePassword(host, realm, null, password); + result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertNull(result[0]); + assertEquals(password, result[1]); + + mWebView.setHttpAuthUsernamePassword(host, realm, userName, null); + result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(userName, result[0]); + assertNull(result[1]); + + mWebView.setHttpAuthUsernamePassword(host, realm, null, null); + result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertNull(result[0]); + assertNull(result[1]); + + mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword); + result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(newUserName, result[0]); + assertEquals(newPassword, result[1]); + } finally { + WebViewDatabase.getInstance(mActivity).clearHttpAuthUsernamePassword(); + } + }); } - @UiThreadTest + @Test public void testWebViewDatabaseAccessHttpAuthUsernamePassword() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - WebViewDatabase webViewDb = WebViewDatabase.getInstance(getActivity()); - try { - webViewDb.clearHttpAuthUsernamePassword(); - - String host = "http://localhost:8080"; - String realm = "testrealm"; - String userName = "user"; - String password = "password"; - - String[] result = - mWebView.getHttpAuthUsernamePassword(host, - realm); - assertNull(result); - - webViewDb.setHttpAuthUsernamePassword(host, realm, userName, password); - result = webViewDb.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(userName, result[0]); - assertEquals(password, result[1]); - - String newPassword = "newpassword"; - webViewDb.setHttpAuthUsernamePassword(host, realm, userName, newPassword); - result = webViewDb.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(userName, result[0]); - assertEquals(newPassword, result[1]); - - String newUserName = "newuser"; - webViewDb.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword); - result = webViewDb.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(newUserName, result[0]); - assertEquals(newPassword, result[1]); - - // the user is set to null, can not change any thing in the future - webViewDb.setHttpAuthUsernamePassword(host, realm, null, password); - result = webViewDb.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertNull(result[0]); - assertEquals(password, result[1]); - - webViewDb.setHttpAuthUsernamePassword(host, realm, userName, null); - result = webViewDb.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(userName, result[0]); - assertNull(result[1]); - - webViewDb.setHttpAuthUsernamePassword(host, realm, null, null); - result = webViewDb.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertNull(result[0]); - assertNull(result[1]); - - webViewDb.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword); - result = webViewDb.getHttpAuthUsernamePassword(host, realm); - assertNotNull(result); - assertEquals(newUserName, result[0]); - assertEquals(newPassword, result[1]); - } finally { - webViewDb.clearHttpAuthUsernamePassword(); - } + WebkitUtils.onMainThreadSync( + () -> { + WebViewDatabase webViewDb = WebViewDatabase.getInstance(mActivity); + try { + webViewDb.clearHttpAuthUsernamePassword(); + + String host = "http://localhost:8080"; + String realm = "testrealm"; + String userName = "user"; + String password = "password"; + + String[] result = mWebView.getHttpAuthUsernamePassword(host, realm); + assertNull(result); + + webViewDb.setHttpAuthUsernamePassword(host, realm, userName, password); + result = webViewDb.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(userName, result[0]); + assertEquals(password, result[1]); + + String newPassword = "newpassword"; + webViewDb.setHttpAuthUsernamePassword(host, realm, userName, newPassword); + result = webViewDb.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(userName, result[0]); + assertEquals(newPassword, result[1]); + + String newUserName = "newuser"; + webViewDb.setHttpAuthUsernamePassword( + host, realm, newUserName, newPassword); + result = webViewDb.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(newUserName, result[0]); + assertEquals(newPassword, result[1]); + + // the user is set to null, can not change any thing in the future + webViewDb.setHttpAuthUsernamePassword(host, realm, null, password); + result = webViewDb.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertNull(result[0]); + assertEquals(password, result[1]); + + webViewDb.setHttpAuthUsernamePassword(host, realm, userName, null); + result = webViewDb.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(userName, result[0]); + assertNull(result[1]); + + webViewDb.setHttpAuthUsernamePassword(host, realm, null, null); + result = webViewDb.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertNull(result[0]); + assertNull(result[1]); + + webViewDb.setHttpAuthUsernamePassword( + host, realm, newUserName, newPassword); + result = webViewDb.getHttpAuthUsernamePassword(host, realm); + assertNotNull(result); + assertEquals(newUserName, result[0]); + assertEquals(newPassword, result[1]); + } finally { + webViewDb.clearHttpAuthUsernamePassword(); + } + }); } + @Test public void testLoadData() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final String firstTitle = "Hello, World!"; final String HTML_CONTENT = - "<html><head><title>" + firstTitle + "</title></head><body></body>" + - "</html>"; - mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT, - "text/html", null); + "<html><head><title>" + firstTitle + "</title></head><body></body>" + "</html>"; + mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT, "text/html", null); assertEquals(firstTitle, mOnUiThread.getTitle()); startWebServer(false); @@ -1111,20 +1108,25 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mOnUiThread.getSettings().setJavaScriptEnabled(true); final String secondTitle = "Foo bar"; mOnUiThread.loadDataAndWaitForCompletion( - "<html><head><title>" + secondTitle + "</title></head><body onload=\"" + - "document.title = " + - "document.getElementById('frame').contentWindow.location.href;" + - "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>", - "text/html", null); - assertEquals("Page title should not change, because it should be an error to access a " - + "cross-site frame's href.", - secondTitle, mOnUiThread.getTitle()); + "<html><head><title>" + + secondTitle + + "</title></head><body onload=\"" + + "document.title = " + + "document.getElementById('frame').contentWindow.location.href;" + + "\"><iframe id=\"frame\" src=\"" + + crossOriginUrl + + "\"></body></html>", + "text/html", + null); + assertEquals( + "Page title should not change, because it should be an error to access a " + + "cross-site frame's href.", + secondTitle, + mOnUiThread.getTitle()); } + @Test public void testLoadDataWithBaseUrl_resolvesRelativeToBaseUrl() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } assertNull(mOnUiThread.getUrl()); String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative @@ -1135,103 +1137,106 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi startWebServer(false); final String baseUrl = mWebServer.getAssetUrl("foo.html"); mWebServer.resetRequestState(); - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl, + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + baseUrl, HTML_HEADER + "<body><img src=\"" + imgUrl + "\"/></body></html>", - "text/html", "UTF-8", null); - assertTrue("The resource request should make it to the server", + "text/html", + "UTF-8", + null); + assertTrue( + "The resource request should make it to the server", mWebServer.wasResourceRequested(imgUrl)); } + @Test public void testLoadDataWithBaseUrl_historyUrl() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final String baseUrl = "http://www.baseurl.com/"; final String historyUrl = "http://www.example.com/"; - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl, - SIMPLE_HTML, - "text/html", "UTF-8", historyUrl); + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + baseUrl, SIMPLE_HTML, "text/html", "UTF-8", historyUrl); assertEquals(historyUrl, mOnUiThread.getUrl()); } + @Test public void testLoadDataWithBaseUrl_nullHistoryUrlShowsAsAboutBlank() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } // Check that reported URL is "about:blank" when supplied history URL // is null. final String baseUrl = "http://www.baseurl.com/"; - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl, - SIMPLE_HTML, - "text/html", "UTF-8", null); + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + baseUrl, SIMPLE_HTML, "text/html", "UTF-8", null); assertEquals("about:blank", mOnUiThread.getUrl()); } + @Test public void testLoadDataWithBaseUrl_javascriptCanAccessOrigin() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } // Test that JavaScript can access content from the same origin as the base URL. mOnUiThread.getSettings().setJavaScriptEnabled(true); startWebServer(false); final String baseUrl = mWebServer.getAssetUrl("foo.html"); final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl, - HTML_HEADER + "<body onload=\"" + - "document.title = document.getElementById('frame').contentWindow.location.href;" + - "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>", - "text/html", "UTF-8", null); + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + baseUrl, + HTML_HEADER + + "<body onload=\"document.title =" + + " document.getElementById('frame').contentWindow.location.href;\"><iframe" + + " id=\"frame\" src=\"" + + crossOriginUrl + + "\"></body></html>", + "text/html", + "UTF-8", + null); assertEquals(crossOriginUrl, mOnUiThread.getTitle()); } + @Test public void testLoadDataWithBaseUrl_dataBaseUrlIgnoresHistoryUrl() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } // Check that when the base URL uses the 'data' scheme, a 'data' scheme URL is used and the // history URL is ignored. final String baseUrl = "data:foo"; final String historyUrl = "http://www.example.com/"; - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl, - SIMPLE_HTML, - "text/html", "UTF-8", historyUrl); + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + baseUrl, SIMPLE_HTML, "text/html", "UTF-8", historyUrl); final String currentUrl = mOnUiThread.getUrl(); - assertEquals("Current URL (" + currentUrl + ") should be a data URI", 0, + assertEquals( + "Current URL (" + currentUrl + ") should be a data URI", + 0, mOnUiThread.getUrl().indexOf("data:text/html")); - assertThat("Current URL (" + currentUrl + ") should contain the simple HTML we loaded", - mOnUiThread.getUrl().indexOf("simple html"), greaterThan(0)); + assertThat( + "Current URL (" + currentUrl + ") should contain the simple HTML we loaded", + mOnUiThread.getUrl().indexOf("simple html"), + greaterThan(0)); } + @Test public void testLoadDataWithBaseUrl_unencodedContentHttpBaseUrl() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } // Check that when a non-data: base URL is used, we treat the String to load as // a raw string and just dump it into the WebView, i.e. not decoding any URL entities. - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("http://www.foo.com", + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + "http://www.foo.com", HTML_HEADER + "<title>Hello World%21</title><body>bar</body></html>", - "text/html", "UTF-8", null); + "text/html", + "UTF-8", + null); assertEquals("Hello World%21", mOnUiThread.getTitle()); } + @Test public void testLoadDataWithBaseUrl_urlEncodedContentDataBaseUrl() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } // Check that when a data: base URL is used, we treat the String to load as a data: URL // and run load steps such as decoding URL entities (i.e., contrary to the test case // above.) - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo", - HTML_HEADER + "<title>Hello World%21</title></html>", "text/html", "UTF-8", null); + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + "data:foo", + HTML_HEADER + "<title>Hello World%21</title></html>", + "text/html", + "UTF-8", + null); assertEquals("Hello World!", mOnUiThread.getTitle()); } + @Test public void testLoadDataWithBaseUrl_nullSafe() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(null, null, null, null, null); assertEquals("about:blank", mOnUiThread.getUrl()); } @@ -1245,7 +1250,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi private String readTextFile(File file, Charset encoding) throws FileNotFoundException, IOException { FileInputStream stream = new FileInputStream(file); - byte[] bytes = new byte[(int)file.length()]; + byte[] bytes = new byte[(int) file.length()]; stream.read(bytes); stream.close(); return new String(bytes, encoding); @@ -1254,26 +1259,24 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi private void doSaveWebArchive(String baseName, boolean autoName, final String expectName) throws Throwable { final Semaphore saving = new Semaphore(0); - ValueCallback<String> callback = new ValueCallback<String>() { - @Override - public void onReceiveValue(String savedName) { - assertEquals(expectName, savedName); - saving.release(); - } - }; + ValueCallback<String> callback = + new ValueCallback<String>() { + @Override + public void onReceiveValue(String savedName) { + assertEquals(expectName, savedName); + saving.release(); + } + }; mOnUiThread.saveWebArchive(baseName, autoName, callback); assertTrue(saving.tryAcquire(WebkitUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS)); } + @Test public void testSaveWebArchive() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - final String testPage = "testSaveWebArchive test page"; - File dir = getActivity().getFilesDir(); + File dir = mActivity.getFilesDir(); String dirStr = dir.toString(); File test = new File(dir, "test.mht"); @@ -1324,16 +1327,14 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } } - private static class WaitForFindResultsListener - implements WebView.FindListener { + private static class WaitForFindResultsListener implements WebView.FindListener { private final SettableFuture<Integer> mFuture; private final WebView mWebView; private final int mMatchesWanted; private final String mStringWanted; private final boolean mRetry; - public WaitForFindResultsListener( - WebView wv, String wanted, int matches, boolean retry) { + WaitForFindResultsListener(WebView wv, String wanted, int matches, boolean retry) { mFuture = SettableFuture.create(); mWebView = wv; mMatchesWanted = matches; @@ -1346,62 +1347,66 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } @Override - public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, - boolean isDoneCounting) { + public void onFindResultReceived( + int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) { try { - assertEquals("WebView.FindListener callbacks should occur on the UI thread", - Looper.myLooper(), Looper.getMainLooper()); + assertEquals( + "WebView.FindListener callbacks should occur on the UI thread", + Looper.myLooper(), + Looper.getMainLooper()); } catch (Throwable t) { mFuture.setException(t); } if (isDoneCounting) { - //If mRetry set to true and matches aren't equal, call findAll again + // If mRetry set to true and matches aren't equal, call findAll again if (mRetry && numberOfMatches != mMatchesWanted) { mWebView.findAll(mStringWanted); - } - else { + } else { mFuture.set(numberOfMatches); } } } } - public void testFindAll() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } + @Test + public void testFindAll() throws Throwable { // Make the page scrollable, so we can detect the scrolling to make sure the // content fully loaded. mOnUiThread.setInitialScale(100); DisplayMetrics metrics = mOnUiThread.getDisplayMetrics(); int dimension = Math.max(metrics.widthPixels, metrics.heightPixels); // create a paragraph high enough to take up the entire screen - String p = "<p style=\"height:" + dimension + "px;\">" + - "Find all instances of find on the page and highlight them.</p>"; + String p = + "<p style=\"height:" + + dimension + + "px;\">" + + "Find all instances of find on the page and highlight them.</p>"; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "</body></html>", "text/html", null); WaitForFindResultsListener l = new WaitForFindResultsListener(mWebView, "find", 2, true); mOnUiThread.setFindListener(l); mOnUiThread.findAll("find"); - assertEquals(2, (int)WebkitUtils.waitForFuture(l.future())); + assertEquals(2, (int) WebkitUtils.waitForFuture(l.future())); } + @Test public void testFindNext() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } // Reset the scaling so that finding the next "all" text will require scrolling. mOnUiThread.setInitialScale(100); DisplayMetrics metrics = mOnUiThread.getDisplayMetrics(); int dimension = Math.max(metrics.widthPixels, metrics.heightPixels); // create a paragraph high enough to take up the entire screen - String p = "<p style=\"height:" + dimension + "px;\">" + - "Find all instances of a word on the page and highlight them.</p>"; + String p = + "<p style=\"height:" + + dimension + + "px;\">" + + "Find all instances of a word on the page and highlight them.</p>"; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + p + "</body></html>", "text/html", null); WaitForFindResultsListener l = new WaitForFindResultsListener(mWebView, "all", 2, true); mOnUiThread.setFindListener(l); @@ -1438,7 +1443,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi // clear the result mOnUiThread.clearMatches(); - getInstrumentation().waitForIdleSync(); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); // can not scroll any more mOnUiThread.findNext(false); @@ -1450,20 +1455,21 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertEquals(mOnUiThread.getScrollY(), previousScrollY); } + @Test public void testDocumentHasImages() throws Exception, Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final class DocumentHasImageCheckHandler extends Handler { private SettableFuture<Integer> mFuture; + public DocumentHasImageCheckHandler(Looper looper) { super(looper); mFuture = SettableFuture.create(); } + @Override public void handleMessage(Message msg) { mFuture.set(msg.arg1); } + public Future<Integer> future() { return mFuture; } @@ -1474,17 +1480,20 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi // Create a handler on the UI thread. final DocumentHasImageCheckHandler handler = - new DocumentHasImageCheckHandler(mWebView.getHandler().getLooper()); - - WebkitUtils.onMainThreadSync(() -> { - mOnUiThread.loadDataAndWaitForCompletion("<html><body><img src=\"" - + imgUrl + "\"/></body></html>", "text/html", null); - Message response = new Message(); - response.setTarget(handler); - assertFalse(handler.future().isDone()); - mWebView.documentHasImages(response); - }); - assertEquals(1, (int)WebkitUtils.waitForFuture(handler.future())); + new DocumentHasImageCheckHandler(mWebView.getHandler().getLooper()); + + WebkitUtils.onMainThreadSync( + () -> { + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body><img src=\"" + imgUrl + "\"/></body></html>", + "text/html", + null); + Message response = new Message(); + response.setTarget(handler); + assertFalse(handler.future().isDone()); + mWebView.documentHasImages(response); + }); + assertEquals(1, (int) WebkitUtils.waitForFuture(handler.future())); } private static void waitForFlingDone(WebViewOnUiThread webview) { @@ -1518,23 +1527,24 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi new ScrollDiffPollingCheck(webview).run(); } + @Test public void testPageScroll() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } DisplayMetrics metrics = mOnUiThread.getDisplayMetrics(); int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels); - String p = "<p style=\"height:" + dimension + "px;\">" + - "Scroll by half the size of the page.</p>"; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + p + "</body></html>", "text/html", null); + String p = + "<p style=\"height:" + + dimension + + "px;\">" + + "Scroll by half the size of the page.</p>"; + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + p + "</body></html>", "text/html", null); // Wait for UI thread to settle and receive page dimentions from renderer // such that we can invoke page down. new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { - return mOnUiThread.pageDown(false); + return mOnUiThread.pageDown(false); } }.run(); @@ -1565,7 +1575,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi // jump to the top assertTrue(mOnUiThread.pageUp(true)); - new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { + new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { return topScrollY == mOnUiThread.getScrollY(); @@ -1573,17 +1583,15 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); } + @Test public void testGetContentHeight() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.loadDataAndWaitForCompletion( - "<html><body></body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion("<html><body></body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { - return mOnUiThread.getScale() != 0 && mOnUiThread.getContentHeight() != 0 - && mOnUiThread.getHeight() != 0; + return mOnUiThread.getScale() != 0 + && mOnUiThread.getContentHeight() != 0 + && mOnUiThread.getHeight() != 0; } }.run(); @@ -1611,14 +1619,17 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi DisplayMetrics metrics = mOnUiThread.getDisplayMetrics(); final float scaleFactor = Math.max(1.0f, 1.0f / mOnUiThread.getScale()); final int pageHeight = - (int)(Math.ceil(Math.max(metrics.widthPixels, metrics.heightPixels) - * scaleFactor)); + (int) + (Math.ceil( + Math.max(metrics.widthPixels, metrics.heightPixels) * scaleFactor)); // set the margin to 0 - final String p = "<p style=\"height:" + pageHeight - + "px;margin:0px auto;\">Get the height of HTML content.</p>"; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "</body></html>", "text/html", null); + final String p = + "<p style=\"height:" + + pageHeight + + "px;margin:0px auto;\">Get the height of HTML content.</p>"; + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { @@ -1627,8 +1638,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); final int extraSpace = mOnUiThread.getContentHeight() - pageHeight; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + p + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + p + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { @@ -1643,53 +1654,57 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); } - @UiThreadTest + @Test public void testPlatformNotifications() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - WebView.enablePlatformNotifications(); - WebView.disablePlatformNotifications(); + WebkitUtils.onMainThreadSync( + () -> { + WebView.enablePlatformNotifications(); + WebView.disablePlatformNotifications(); + }); } - @UiThreadTest + @Test public void testAccessPluginList() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - assertNotNull(WebView.getPluginList()); + WebkitUtils.onMainThreadSync( + () -> { + assertNotNull(WebView.getPluginList()); - // can not find a way to install plugins - mWebView.refreshPlugins(false); + // can not find a way to install plugins + mWebView.refreshPlugins(false); + }); } - @UiThreadTest + @Test public void testDestroy() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - // Create a new WebView, since we cannot call destroy() on a view in the hierarchy - WebView localWebView = new WebView(getActivity()); - localWebView.destroy(); + WebkitUtils.onMainThreadSync( + () -> { + // Create a new WebView, since we cannot call destroy() on a view in the + // hierarchy + WebView localWebView = new WebView(mActivity); + localWebView.destroy(); + }); } + @Test public void testFlingScroll() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } DisplayMetrics metrics = mOnUiThread.getDisplayMetrics(); final int dimension = 10 * Math.max(metrics.widthPixels, metrics.heightPixels); - String p = "<p style=\"height:" + dimension + "px;" + - "width:" + dimension + "px\">Test fling scroll.</p>"; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "</body></html>", "text/html", null); + String p = + "<p style=\"height:" + + dimension + + "px;" + + "width:" + + dimension + + "px\">Test fling scroll.</p>"; + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { return mOnUiThread.getContentHeight() >= dimension; } }.run(); - getInstrumentation().waitForIdleSync(); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); final int previousScrollX = mOnUiThread.getScrollX(); final int previousScrollY = mOnUiThread.getScrollY(); @@ -1699,24 +1714,27 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { - return mOnUiThread.getScrollX() > previousScrollX && - mOnUiThread.getScrollY() > previousScrollY; + return mOnUiThread.getScrollX() > previousScrollX + && mOnUiThread.getScrollY() > previousScrollY; } }.run(); } + @Test public void testRequestFocusNodeHref() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } startWebServer(false); + String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1); String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); - final String links = "<DL><p><DT><A HREF=\"" + url1 - + "\">HTML_URL1</A><DT><A HREF=\"" + url2 - + "\">HTML_URL2</A></DL><p>"; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + links + "</body></html>", "text/html", null); - getInstrumentation().waitForIdleSync(); + final String links = + "<DL><p><DT><A HREF=\"" + + url1 + + "\">HTML_URL1</A><DT><A HREF=\"" + + url2 + + "\">HTML_URL2</A></DL><p>"; + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + links + "</body></html>", "text/html", null); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper()); final Message hrefMsg = new Message(); @@ -1724,7 +1742,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi // focus on first link handler.reset(); - getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB); + InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB); mOnUiThread.requestFocusNodeHref(hrefMsg); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override @@ -1749,7 +1767,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi handler.reset(); final Message hrefMsg2 = new Message(); hrefMsg2.setTarget(handler); - getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB); + InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB); mOnUiThread.requestFocusNodeHref(hrefMsg2); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override @@ -1757,8 +1775,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi boolean done = false; final String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); if (handler.hasCalledHandleMessage()) { - if (handler.mResultUrl != null && - handler.mResultUrl.equals(url2)) { + if (handler.mResultUrl != null && handler.mResultUrl.equals(url2)) { done = true; } else { handler.reset(); @@ -1775,10 +1792,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mOnUiThread.requestFocusNodeHref(null); } + @Test public void testRequestImageRef() throws Exception, Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final class ImageLoaded { public SettableFuture<Void> mImageLoaded = SettableFuture.create(); @@ -1799,19 +1814,22 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.LARGE_IMG_URL); mOnUiThread.loadDataAndWaitForCompletion( "<html><head><title>Title</title><style type='text/css'>" - + "%23imgElement { -webkit-transform: translate3d(0,0,1); }" - + "%23imgElement.finish { -webkit-transform: translate3d(0,0,0);" - + " -webkit-transition-duration: 1ms; }</style>" - + "<script type='text/javascript'>function imgLoad() {" - + "imgElement = document.getElementById('imgElement');" - + "imgElement.addEventListener('webkitTransitionEnd'," - + "function(e) { imageLoaded.loaded(); });" - + "imgElement.className = 'finish';}</script>" - + "</head><body><img id='imgElement' src='" + imgUrl - + "' width='100%' height='100%' onLoad='imgLoad()'/>" - + "</body></html>", "text/html", null); + + "%23imgElement { -webkit-transform: translate3d(0,0,1); }" + + "%23imgElement.finish { -webkit-transform: translate3d(0,0,0);" + + " -webkit-transition-duration: 1ms; }</style>" + + "<script type='text/javascript'>function imgLoad() {" + + "imgElement = document.getElementById('imgElement');" + + "imgElement.addEventListener('webkitTransitionEnd'," + + "function(e) { imageLoaded.loaded(); });" + + "imgElement.className = 'finish';}</script>" + + "</head><body><img id='imgElement' src='" + + imgUrl + + "' width='100%' height='100%' onLoad='imgLoad()'/>" + + "</body></html>", + "text/html", + null); WebkitUtils.waitForFuture(imageLoaded.future()); - getInstrumentation().waitForIdleSync(); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper()); final Message msg = new Message(); @@ -1824,10 +1842,11 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi int middleY = location[1] + mOnUiThread.getWebView().getHeight() / 2; long time = SystemClock.uptimeMillis(); - getInstrumentation().sendPointerSync( - MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, - middleX, middleY, 0)); - getInstrumentation().waitForIdleSync(); + InstrumentationRegistry.getInstrumentation() + .sendPointerSync( + MotionEvent.obtain( + time, time, MotionEvent.ACTION_DOWN, middleX, middleY, 0)); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); mOnUiThread.requestImageRef(msg); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override @@ -1849,23 +1868,22 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertEquals(imgUrl, handler.mResultUrl); } - @UiThreadTest + @Test public void testDebugDump() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mWebView.debugDump(); + WebkitUtils.onMainThreadSync( + () -> { + mWebView.debugDump(); + }); } + @Test public void testGetHitTestResult() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - final String anchor = "<p><a href=\"" + TestHtmlConstants.EXT_WEB_URL1 - + "\">normal anchor</a></p>"; + final String anchor = + "<p><a href=\"" + TestHtmlConstants.EXT_WEB_URL1 + "\">normal anchor</a></p>"; final String blankAnchor = "<p><a href=\"\">blank anchor</a></p>"; - final String form = "<p><form><input type=\"text\" name=\"Test\"><br>" - + "<input type=\"submit\" value=\"Submit\"></form></p>"; + final String form = + "<p><form><input type=\"text\" name=\"Test\"><br>" + + "<input type=\"submit\" value=\"Submit\"></form></p>"; String phoneNo = "3106984000"; final String tel = "<p><a href=\"tel:" + phoneNo + "\">Phone</a></p>"; String email = "test@gmail.com"; @@ -1873,10 +1891,20 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi String location = "shanghai"; final String geo = "<p><a href=\"geo:0,0?q=" + location + "\">Location</a></p>"; - mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("fake://home", - "<html><body>" + anchor + blankAnchor + form + tel + mailto + - geo + "</body></html>", "text/html", "UTF-8", null); - getInstrumentation().waitForIdleSync(); + mOnUiThread.loadDataWithBaseURLAndWaitForCompletion( + "fake://home", + "<html><body>" + + anchor + + blankAnchor + + form + + tel + + mailto + + geo + + "</body></html>", + "text/html", + "UTF-8", + null); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); // anchor moveFocusDown(); @@ -1921,16 +1949,13 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertEquals(location, hitTestResult.getExtra()); } + @Test public void testSetInitialScale() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } final String p = "<p style=\"height:1000px;width:1000px\">Test setInitialScale.</p>"; - final float defaultScale = - getActivity().getResources().getDisplayMetrics().density; + final float defaultScale = mActivity.getResources().getDisplayMetrics().density; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override @@ -1941,8 +1966,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mOnUiThread.setInitialScale(0); // modify content to fool WebKit into re-loading - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "2" + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "2" + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override @@ -1952,8 +1977,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); mOnUiThread.setInitialScale(50); - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "3" + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "3" + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override @@ -1963,8 +1988,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); mOnUiThread.setInitialScale(0); - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "4" + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "4" + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override @@ -1974,112 +1999,112 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); } - @UiThreadTest + @Test public void testClearHistory() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } startWebServer(false); - String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1); - String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); - String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3); - mOnUiThread.loadUrlAndWaitForCompletion(url1); - pollingCheckWebBackForwardList(url1, 0, 1); + WebkitUtils.onMainThreadSync( + () -> { + String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1); + String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); + String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3); - mOnUiThread.loadUrlAndWaitForCompletion(url2); - pollingCheckWebBackForwardList(url2, 1, 2); + mOnUiThread.loadUrlAndWaitForCompletion(url1); + pollingCheckWebBackForwardList(url1, 0, 1); - mOnUiThread.loadUrlAndWaitForCompletion(url3); - pollingCheckWebBackForwardList(url3, 2, 3); + mOnUiThread.loadUrlAndWaitForCompletion(url2); + pollingCheckWebBackForwardList(url2, 1, 2); - mWebView.clearHistory(); + mOnUiThread.loadUrlAndWaitForCompletion(url3); + pollingCheckWebBackForwardList(url3, 2, 3); - // only current URL is left after clearing - pollingCheckWebBackForwardList(url3, 0, 1); + mWebView.clearHistory(); + + // only current URL is left after clearing + pollingCheckWebBackForwardList(url3, 0, 1); + }); } - @UiThreadTest + @Test public void testSaveAndRestoreState() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - assertNull("Should return null when there's nothing to save", - mWebView.saveState(new Bundle())); - startWebServer(false); - String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1); - String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); - String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3); - - // make a history list - mOnUiThread.loadUrlAndWaitForCompletion(url1); - pollingCheckWebBackForwardList(url1, 0, 1); - mOnUiThread.loadUrlAndWaitForCompletion(url2); - pollingCheckWebBackForwardList(url2, 1, 2); - mOnUiThread.loadUrlAndWaitForCompletion(url3); - pollingCheckWebBackForwardList(url3, 2, 3); - - // save the list - Bundle bundle = new Bundle(); - WebBackForwardList saveList = mWebView.saveState(bundle); - assertNotNull(saveList); - assertEquals(3, saveList.getSize()); - assertEquals(2, saveList.getCurrentIndex()); - assertEquals(url1, saveList.getItemAtIndex(0).getUrl()); - assertEquals(url2, saveList.getItemAtIndex(1).getUrl()); - assertEquals(url3, saveList.getItemAtIndex(2).getUrl()); - - // change the content to a new "blank" web view without history - final WebView newWebView = new WebView(getActivity()); - - WebBackForwardList copyListBeforeRestore = newWebView.copyBackForwardList(); - assertNotNull(copyListBeforeRestore); - assertEquals(0, copyListBeforeRestore.getSize()); - - // restore the list - final WebBackForwardList restoreList = newWebView.restoreState(bundle); - assertNotNull(restoreList); - assertEquals(3, restoreList.getSize()); - assertEquals(2, saveList.getCurrentIndex()); - - // wait for the list items to get inflated - new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { - @Override - protected boolean check() { - return restoreList.getItemAtIndex(0).getUrl() != null && - restoreList.getItemAtIndex(1).getUrl() != null && - restoreList.getItemAtIndex(2).getUrl() != null; - } - }.run(); - assertEquals(url1, restoreList.getItemAtIndex(0).getUrl()); - assertEquals(url2, restoreList.getItemAtIndex(1).getUrl()); - assertEquals(url3, restoreList.getItemAtIndex(2).getUrl()); - WebBackForwardList copyListAfterRestore = newWebView.copyBackForwardList(); - assertNotNull(copyListAfterRestore); - assertEquals(3, copyListAfterRestore.getSize()); - assertEquals(2, copyListAfterRestore.getCurrentIndex()); - assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl()); - assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl()); - assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl()); - - newWebView.destroy(); + WebkitUtils.onMainThreadSync( + () -> { + assertNull( + "Should return null when there's nothing to save", + mWebView.saveState(new Bundle())); + + String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1); + String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2); + String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3); + + // make a history list + mOnUiThread.loadUrlAndWaitForCompletion(url1); + pollingCheckWebBackForwardList(url1, 0, 1); + mOnUiThread.loadUrlAndWaitForCompletion(url2); + pollingCheckWebBackForwardList(url2, 1, 2); + mOnUiThread.loadUrlAndWaitForCompletion(url3); + pollingCheckWebBackForwardList(url3, 2, 3); + + // save the list + Bundle bundle = new Bundle(); + WebBackForwardList saveList = mWebView.saveState(bundle); + assertNotNull(saveList); + assertEquals(3, saveList.getSize()); + assertEquals(2, saveList.getCurrentIndex()); + assertEquals(url1, saveList.getItemAtIndex(0).getUrl()); + assertEquals(url2, saveList.getItemAtIndex(1).getUrl()); + assertEquals(url3, saveList.getItemAtIndex(2).getUrl()); + + // change the content to a new "blank" web view without history + final WebView newWebView = new WebView(mActivity); + + WebBackForwardList copyListBeforeRestore = newWebView.copyBackForwardList(); + assertNotNull(copyListBeforeRestore); + assertEquals(0, copyListBeforeRestore.getSize()); + + // restore the list + final WebBackForwardList restoreList = newWebView.restoreState(bundle); + assertNotNull(restoreList); + assertEquals(3, restoreList.getSize()); + assertEquals(2, saveList.getCurrentIndex()); + + // wait for the list items to get inflated + new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { + @Override + protected boolean check() { + return restoreList.getItemAtIndex(0).getUrl() != null + && restoreList.getItemAtIndex(1).getUrl() != null + && restoreList.getItemAtIndex(2).getUrl() != null; + } + }.run(); + assertEquals(url1, restoreList.getItemAtIndex(0).getUrl()); + assertEquals(url2, restoreList.getItemAtIndex(1).getUrl()); + assertEquals(url3, restoreList.getItemAtIndex(2).getUrl()); + + WebBackForwardList copyListAfterRestore = newWebView.copyBackForwardList(); + assertNotNull(copyListAfterRestore); + assertEquals(3, copyListAfterRestore.getSize()); + assertEquals(2, copyListAfterRestore.getCurrentIndex()); + assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl()); + assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl()); + assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl()); + + newWebView.destroy(); + }); } + @Test public void testRequestChildRectangleOnScreen() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - // It is needed to make test pass on some devices. mOnUiThread.setLayoutToMatchParent(); DisplayMetrics metrics = mOnUiThread.getDisplayMetrics(); final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels); String p = "<p style=\"height:" + dimension + "px;width:" + dimension + "px\"> </p>"; - mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p - + "</body></html>", "text/html", null); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><body>" + p + "</body></html>", "text/html", null); new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { @@ -2097,11 +2122,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertThat(mOnUiThread.getScrollY(), greaterThan(origY)); } + @Test public void testSetDownloadListener() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - final SettableFuture<Void> downloadStartFuture = SettableFuture.create(); final class MyDownloadListener implements DownloadListener { public String url; @@ -2110,8 +2132,12 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi public String contentDisposition; @Override - public void onDownloadStart(String url, String userAgent, String contentDisposition, - String mimetype, long contentLength) { + public void onDownloadStart( + String url, + String userAgent, + String contentDisposition, + String mimetype, + long contentLength) { this.url = url; this.mimeType = mimetype; this.contentLength = contentLength; @@ -2135,9 +2161,10 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mOnUiThread.getSettings().setJavaScriptEnabled(true); mOnUiThread.loadDataAndWaitForCompletion( "<html><body onload=\"window.location = \'" + url + "\'\"></body></html>", - "text/html", null); + "text/html", + null); // Wait for layout to complete before setting focus. - getInstrumentation().waitForIdleSync(); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); WebkitUtils.waitForFuture(downloadStartFuture); assertEquals(url, listener.url); @@ -2146,28 +2173,26 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi assertEquals(mimeType, listener.mimeType); } - @UiThreadTest + @Test public void testSetLayoutParams() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(600, 800); - mWebView.setLayoutParams(params); - assertSame(params, mWebView.getLayoutParams()); + WebkitUtils.onMainThreadSync( + () -> { + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(600, 800); + mWebView.setLayoutParams(params); + assertSame(params, mWebView.getLayoutParams()); + }); } - @UiThreadTest + @Test public void testSetMapTrackballToArrowKeys() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mWebView.setMapTrackballToArrowKeys(true); + WebkitUtils.onMainThreadSync( + () -> { + mWebView.setMapTrackballToArrowKeys(true); + }); } + @Test public void testSetNetworkAvailable() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } WebSettings settings = mOnUiThread.getSettings(); settings.setJavaScriptEnabled(true); startWebServer(false); @@ -2197,42 +2222,39 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); } + @Test public void testSetWebChromeClient() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - final SettableFuture<Void> future = SettableFuture.create(); - mOnUiThread.setWebChromeClient(new WaitForProgressClient(mOnUiThread) { - @Override - public void onProgressChanged(WebView view, int newProgress) { - super.onProgressChanged(view, newProgress); - future.set(null); - } - }); - getInstrumentation().waitForIdleSync(); + mOnUiThread.setWebChromeClient( + new WaitForProgressClient(mOnUiThread) { + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + future.set(null); + } + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); assertFalse(future.isDone()); startWebServer(false); final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL); mOnUiThread.loadUrlAndWaitForCompletion(url); - getInstrumentation().waitForIdleSync(); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); WebkitUtils.waitForFuture(future); } + @Test public void testPauseResumeTimers() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } class Monitor { private boolean mIsUpdated; @JavascriptInterface public synchronized void update() { - mIsUpdated = true; + mIsUpdated = true; notify(); } + public synchronized boolean waitForUpdate() { while (!mIsUpdated) { try { @@ -2249,25 +2271,28 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi mIsUpdated = false; return true; } - }; + } + final Monitor monitor = new Monitor(); - final String updateMonitorHtml = "<html>" + - "<body onload=\"monitor.update();\"></body></html>"; + final String updateMonitorHtml = + "<html>" + "<body onload=\"monitor.update();\"></body></html>"; // Test that JavaScript is executed even with timers paused. - WebkitUtils.onMainThreadSync(() -> { - mWebView.getSettings().setJavaScriptEnabled(true); - mWebView.addJavascriptInterface(monitor, "monitor"); - mWebView.pauseTimers(); - mOnUiThread.loadDataAndWaitForCompletion(updateMonitorHtml, - "text/html", null); - }); + WebkitUtils.onMainThreadSync( + () -> { + mWebView.getSettings().setJavaScriptEnabled(true); + mWebView.addJavascriptInterface(monitor, "monitor"); + mWebView.pauseTimers(); + mOnUiThread.loadDataAndWaitForCompletion(updateMonitorHtml, "text/html", null); + }); assertTrue(monitor.waitForUpdate()); // Start a timer and test that it does not fire. mOnUiThread.loadDataAndWaitForCompletion( - "<html><body onload='setTimeout(function(){monitor.update();},100)'>" + - "</body></html>", "text/html", null); + "<html><body onload='setTimeout(function(){monitor.update();},100)'>" + + "</body></html>", + "text/html", + null); assertFalse(monitor.waitForUpdate()); // Resume timers and test that the timer fires. @@ -2276,37 +2301,31 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } // verify query parameters can be passed correctly to android asset files + @Test public void testAndroidAssetQueryParam() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - WebSettings settings = mOnUiThread.getSettings(); settings.setJavaScriptEnabled(true); // test passing a parameter - String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.PARAM_ASSET_URL+"?val=SUCCESS"); + String fileUrl = + TestHtmlConstants.getFileUrl(TestHtmlConstants.PARAM_ASSET_URL + "?val=SUCCESS"); mOnUiThread.loadUrlAndWaitForCompletion(fileUrl); assertEquals("SUCCESS", mOnUiThread.getTitle()); } // verify anchors work correctly for android asset files + @Test public void testAndroidAssetAnchor() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - WebSettings settings = mOnUiThread.getSettings(); settings.setJavaScriptEnabled(true); // test using an anchor - String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.ANCHOR_ASSET_URL+"#anchor"); + String fileUrl = + TestHtmlConstants.getFileUrl(TestHtmlConstants.ANCHOR_ASSET_URL + "#anchor"); mOnUiThread.loadUrlAndWaitForCompletion(fileUrl); assertEquals("anchor", mOnUiThread.getTitle()); } + @Test public void testEvaluateJavascript() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } mOnUiThread.getSettings().setJavaScriptEnabled(true); mOnUiThread.loadUrlAndWaitForCompletion("about:blank"); @@ -2325,26 +2344,27 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } // Verify Print feature can create a PDF file with a correct preamble. + @Test public void testPrinting() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" + - "<body>foo</body></html>", - "text/html", null); - final PrintDocumentAdapter adapter = mOnUiThread.createPrintDocumentAdapter(); + mOnUiThread.loadDataAndWaitForCompletion( + "<html><head></head>" + "<body>foo</body></html>", "text/html", null); + final PrintDocumentAdapter adapter = mOnUiThread.createPrintDocumentAdapter(); printDocumentStart(adapter); - PrintAttributes attributes = new PrintAttributes.Builder() - .setMediaSize(PrintAttributes.MediaSize.ISO_A4) - .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300)) - .setMinMargins(PrintAttributes.Margins.NO_MARGINS) - .build(); - final WebViewCtsActivity activity = getActivity(); + PrintAttributes attributes = + new PrintAttributes.Builder() + .setMediaSize(PrintAttributes.MediaSize.ISO_A4) + .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300)) + .setMinMargins(PrintAttributes.Margins.NO_MARGINS) + .build(); + final WebViewCtsActivity activity = mActivity; final File file = activity.getFileStreamPath(PRINTER_TEST_FILE); - final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file, - ParcelFileDescriptor.parseMode("w")); + final ParcelFileDescriptor descriptor = + ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode("w")); final SettableFuture<Void> result = SettableFuture.create(); - printDocumentLayout(adapter, null, attributes, + printDocumentLayout( + adapter, + null, + attributes, new LayoutResultCallback() { // Called on UI thread @Override @@ -2369,44 +2389,46 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } // Verify Print feature can create a PDF file with correct number of pages. + @Test public void testPrintingPagesCount() throws Throwable { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } String content = "<html><head></head><body>"; for (int i = 0; i < 500; ++i) { content += "<br />abcdefghijk<br />"; } content += "</body></html>"; mOnUiThread.loadDataAndWaitForCompletion(content, "text/html", null); - final PrintDocumentAdapter adapter = mOnUiThread.createPrintDocumentAdapter(); + final PrintDocumentAdapter adapter = mOnUiThread.createPrintDocumentAdapter(); printDocumentStart(adapter); - PrintAttributes attributes = new PrintAttributes.Builder() - .setMediaSize(PrintAttributes.MediaSize.ISO_A4) - .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300)) - .setMinMargins(PrintAttributes.Margins.NO_MARGINS) - .build(); - final WebViewCtsActivity activity = getActivity(); + PrintAttributes attributes = + new PrintAttributes.Builder() + .setMediaSize(PrintAttributes.MediaSize.ISO_A4) + .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300)) + .setMinMargins(PrintAttributes.Margins.NO_MARGINS) + .build(); + final WebViewCtsActivity activity = mActivity; final File file = activity.getFileStreamPath(PRINTER_TEST_FILE); - final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file, - ParcelFileDescriptor.parseMode("w")); + final ParcelFileDescriptor descriptor = + ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode("w")); final SettableFuture<Void> result = SettableFuture.create(); - printDocumentLayout(adapter, null, attributes, + printDocumentLayout( + adapter, + null, + attributes, new LayoutResultCallback() { // Called on UI thread @Override public void onLayoutFinished(PrintDocumentInfo info, boolean changed) { - PageRange[] pageRanges = new PageRange[] { - new PageRange(1, 1), new PageRange(4, 7) - }; + PageRange[] pageRanges = + new PageRange[] {new PageRange(1, 1), new PageRange(4, 7)}; savePrintedPage(adapter, descriptor, pageRanges, result); } }); try { WebkitUtils.waitForFuture(result); assertThat(file.length(), greaterThan(0L)); - PdfRenderer renderer = new PdfRenderer( - ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)); + PdfRenderer renderer = + new PdfRenderer( + ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)); assertEquals(5, renderer.getPageCount()); } finally { descriptor.close(); @@ -2419,34 +2441,35 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi * androidx.webkit.WebViewCompatTest#testVisualStateCallbackCalled. Modifications to this test * should be reflected in that test as necessary. See http://go/modifying-webview-cts. */ + @Test public void testVisualStateCallbackCalled() throws Exception { // Check that the visual state callback is called correctly. - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - final long kRequest = 100; mOnUiThread.loadUrl("about:blank"); final SettableFuture<Long> visualStateFuture = SettableFuture.create(); - mOnUiThread.postVisualStateCallback(kRequest, new VisualStateCallback() { - public void onComplete(long requestId) { - visualStateFuture.set(requestId); - } - }); + mOnUiThread.postVisualStateCallback( + kRequest, + new VisualStateCallback() { + public void onComplete(long requestId) { + visualStateFuture.set(requestId); + } + }); assertEquals(kRequest, (long) WebkitUtils.waitForFuture(visualStateFuture)); } private static boolean setSafeBrowsingAllowlistSync(List<String> allowlist) { final SettableFuture<Boolean> safeBrowsingAllowlistFuture = SettableFuture.create(); - WebView.setSafeBrowsingWhitelist(allowlist, new ValueCallback<Boolean>() { - @Override - public void onReceiveValue(Boolean success) { - safeBrowsingAllowlistFuture.set(success); - } - }); + WebView.setSafeBrowsingWhitelist( + allowlist, + new ValueCallback<Boolean>() { + @Override + public void onReceiveValue(Boolean success) { + safeBrowsingAllowlistFuture.set(success); + } + }); return WebkitUtils.waitForFuture(safeBrowsingAllowlistFuture); } @@ -2456,11 +2479,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi * Modifications to this test should be reflected in that test as necessary. See * http://go/modifying-webview-cts. */ + @Test public void testSetSafeBrowsingAllowlistWithMalformedList() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - List allowlist = new ArrayList<String>(); // Protocols are not supported in the allowlist allowlist.add("http://google.com"); @@ -2469,33 +2489,35 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi /** * This should remain functionally equivalent to - * androidx.webkit.WebViewCompatTest#testSetSafeBrowsingAllowlistWithValidList. Modifications - * to this test should be reflected in that test as necessary. See - * http://go/modifying-webview-cts. + * androidx.webkit.WebViewCompatTest#testSetSafeBrowsingAllowlistWithValidList. Modifications to + * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts. */ + @Test public void testSetSafeBrowsingAllowlistWithValidList() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - List allowlist = new ArrayList<String>(); allowlist.add("safe-browsing"); assertTrue("Valid allowlist should be successful", setSafeBrowsingAllowlistSync(allowlist)); final SettableFuture<Void> pageFinishedFuture = SettableFuture.create(); - mOnUiThread.setWebViewClient(new WebViewClient() { - @Override - public void onPageFinished(WebView view, String url) { - pageFinishedFuture.set(null); - } + mOnUiThread.setWebViewClient( + new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + pageFinishedFuture.set(null); + } - @Override - public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType, - SafeBrowsingResponse callback) { - pageFinishedFuture.setException(new IllegalStateException( - "Should not invoke onSafeBrowsingHit for " + request.getUrl())); - } - }); + @Override + public void onSafeBrowsingHit( + WebView view, + WebResourceRequest request, + int threatType, + SafeBrowsingResponse callback) { + pageFinishedFuture.setException( + new IllegalStateException( + "Should not invoke onSafeBrowsingHit for " + + request.getUrl())); + } + }); mOnUiThread.loadUrl("chrome://safe-browsing/match?type=malware"); @@ -2508,24 +2530,24 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi * androidx.webkit.WebViewCompatTest#testGetWebViewClient. Modifications to this test should be * reflected in that test as necessary. See http://go/modifying-webview-cts. */ - @UiThreadTest + @Test public void testGetWebViewClient() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - // getWebViewClient should return a default WebViewClient if it hasn't been set yet - WebView webView = new WebView(getActivity()); - WebViewClient client = webView.getWebViewClient(); - assertNotNull(client); - assertTrue(client instanceof WebViewClient); - - // getWebViewClient should return the client after it has been set - WebViewClient client2 = new WebViewClient(); - assertNotSame(client, client2); - webView.setWebViewClient(client2); - assertSame(client2, webView.getWebViewClient()); - webView.destroy(); + WebkitUtils.onMainThreadSync( + () -> { + // getWebViewClient should return a default WebViewClient if it hasn't been set + // yet + WebView webView = new WebView(mActivity); + WebViewClient client = webView.getWebViewClient(); + assertNotNull(client); + assertTrue(client instanceof WebViewClient); + + // getWebViewClient should return the client after it has been set + WebViewClient client2 = new WebViewClient(); + assertNotSame(client, client2); + webView.setWebViewClient(client2); + assertSame(client2, webView.getWebViewClient()); + webView.destroy(); + }); } /** @@ -2533,56 +2555,48 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi * androidx.webkit.WebViewCompatTest#testGetWebChromeClient. Modifications to this test should * be reflected in that test as necessary. See http://go/modifying-webview-cts. */ - @UiThreadTest + @Test public void testGetWebChromeClient() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - // getWebChromeClient should return null if the client hasn't been set yet - WebView webView = new WebView(getActivity()); - WebChromeClient client = webView.getWebChromeClient(); - assertNull(client); - - // getWebChromeClient should return the client after it has been set - WebChromeClient client2 = new WebChromeClient(); - assertNotSame(client, client2); - webView.setWebChromeClient(client2); - assertSame(client2, webView.getWebChromeClient()); - webView.destroy(); + WebkitUtils.onMainThreadSync( + () -> { + // getWebChromeClient should return null if the client hasn't been set yet + WebView webView = new WebView(mActivity); + WebChromeClient client = webView.getWebChromeClient(); + assertNull(client); + + // getWebChromeClient should return the client after it has been set + WebChromeClient client2 = new WebChromeClient(); + assertNotSame(client, client2); + webView.setWebChromeClient(client2); + assertSame(client2, webView.getWebChromeClient()); + webView.destroy(); + }); } - @UiThreadTest + @Test public void testSetCustomTextClassifier() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - class CustomTextClassifier implements TextClassifier { @Override public TextSelection suggestSelection( - CharSequence text, - int startIndex, - int endIndex, - LocaleList defaultLocales) { + CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) { return new TextSelection.Builder(0, 1).build(); } @Override public TextClassification classifyText( - CharSequence text, - int startIndex, - int endIndex, - LocaleList defaultLocales) { + CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) { return new TextClassification.Builder().build(); } - }; + } - TextClassifier classifier = new CustomTextClassifier(); - WebView webView = new WebView(getActivity()); - webView.setTextClassifier(classifier); - assertSame(webView.getTextClassifier(), classifier); - webView.destroy(); + WebkitUtils.onMainThreadSync( + () -> { + TextClassifier classifier = new CustomTextClassifier(); + WebView webView = new WebView(mActivity); + webView.setTextClassifier(classifier); + assertSame(webView.getTextClassifier(), classifier); + webView.destroy(); + }); } private static class MockContext extends ContextWrapper { @@ -2604,23 +2618,25 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi /** * This should remain functionally equivalent to - * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingUseApplicationContext. Modifications to - * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts. + * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingUseApplicationContext. Modifications + * to this test should be reflected in that test as necessary. See + * http://go/modifying-webview-cts. */ + @Test public void testStartSafeBrowsingUseApplicationContext() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - final MockContext ctx = new MockContext(getActivity()); + final MockContext ctx = + new MockContext( + ApplicationProvider.getApplicationContext().getApplicationContext()); final SettableFuture<Boolean> startSafeBrowsingFuture = SettableFuture.create(); - WebView.startSafeBrowsing(ctx, new ValueCallback<Boolean>() { - @Override - public void onReceiveValue(Boolean value) { - startSafeBrowsingFuture.set(ctx.wasGetApplicationContextCalled()); - return; - } - }); + WebView.startSafeBrowsing( + ctx, + new ValueCallback<Boolean>() { + @Override + public void onReceiveValue(Boolean value) { + startSafeBrowsingFuture.set(ctx.wasGetApplicationContextCalled()); + return; + } + }); assertTrue(WebkitUtils.waitForFuture(startSafeBrowsingFuture)); } @@ -2630,40 +2646,40 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi * Modifications to this test should be reflected in that test as necessary. See * http://go/modifying-webview-cts. */ + @Test public void testStartSafeBrowsingWithNullCallbackDoesntCrash() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - - WebView.startSafeBrowsing(getActivity().getApplicationContext(), null); + WebView.startSafeBrowsing( + ApplicationProvider.getApplicationContext().getApplicationContext(), null); } /** * This should remain functionally equivalent to - * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingInvokesCallback. Modifications to - * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts. + * androidx.webkit.WebViewCompatTest#testStartSafeBrowsingInvokesCallback. Modifications to this + * test should be reflected in that test as necessary. See http://go/modifying-webview-cts. */ + @Test public void testStartSafeBrowsingInvokesCallback() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - final SettableFuture<Boolean> startSafeBrowsingFuture = SettableFuture.create(); - WebView.startSafeBrowsing(getActivity().getApplicationContext(), + WebView.startSafeBrowsing( + ApplicationProvider.getApplicationContext().getApplicationContext(), new ValueCallback<Boolean>() { - @Override - public void onReceiveValue(Boolean value) { - startSafeBrowsingFuture.set(Looper.getMainLooper().isCurrentThread()); - return; - } - }); + @Override + public void onReceiveValue(Boolean value) { + startSafeBrowsingFuture.set(Looper.getMainLooper().isCurrentThread()); + return; + } + }); assertTrue(WebkitUtils.waitForFuture(startSafeBrowsingFuture)); } - private void savePrintedPage(final PrintDocumentAdapter adapter, - final ParcelFileDescriptor descriptor, final PageRange[] pageRanges, + private void savePrintedPage( + final PrintDocumentAdapter adapter, + final ParcelFileDescriptor descriptor, + final PageRange[] pageRanges, final SettableFuture<Void> result) { - adapter.onWrite(pageRanges, descriptor, + adapter.onWrite( + pageRanges, + descriptor, new CancellationSignal(), new WriteResultCallback() { @Override @@ -2679,18 +2695,26 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } private void printDocumentStart(final PrintDocumentAdapter adapter) { - WebkitUtils.onMainThreadSync(() -> { - adapter.onStart(); - }); + WebkitUtils.onMainThreadSync( + () -> { + adapter.onStart(); + }); } - private void printDocumentLayout(final PrintDocumentAdapter adapter, - final PrintAttributes oldAttributes, final PrintAttributes newAttributes, + private void printDocumentLayout( + final PrintDocumentAdapter adapter, + final PrintAttributes oldAttributes, + final PrintAttributes newAttributes, final LayoutResultCallback layoutResultCallback) { - WebkitUtils.onMainThreadSync(() -> { - adapter.onLayout(oldAttributes, newAttributes, new CancellationSignal(), - layoutResultCallback, null); - }); + WebkitUtils.onMainThreadSync( + () -> { + adapter.onLayout( + oldAttributes, + newAttributes, + new CancellationSignal(), + layoutResultCallback, + null); + }); } private static class HrefCheckHandler extends Handler { @@ -2710,7 +2734,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi return mResultUrl; } - public void reset(){ + public void reset() { mResultUrl = null; mHadRecieved = false; } @@ -2724,13 +2748,13 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi private void moveFocusDown() throws Throwable { // send down key and wait for idle - getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB); + InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB); // waiting for idle isn't always sufficient for the key to be fully processed Thread.sleep(500); } - private void pollingCheckWebBackForwardList(final String currUrl, final int currIndex, - final int size) { + private void pollingCheckWebBackForwardList( + final String currUrl, final int currIndex, final int size) { new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { @Override protected boolean check() { @@ -2740,8 +2764,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi }.run(); } - private boolean checkWebBackForwardList(WebBackForwardList list, String currUrl, - int currIndex, int size) { + private boolean checkWebBackForwardList( + WebBackForwardList list, String currUrl, int currIndex, int size) { return (list != null) && (list.getSize() == size) && (list.getCurrentIndex() == currIndex) @@ -2750,8 +2774,7 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi private void assertGoBackOrForwardBySteps(boolean expected, int steps) { // skip if steps equals to 0 - if (steps == 0) - return; + if (steps == 0) return; int start = steps > 0 ? 1 : steps; int end = steps > 0 ? steps : -1; @@ -2770,15 +2793,14 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } private boolean isPictureFilledWithColor(Picture picture, int color) { - if (picture.getWidth() == 0 || picture.getHeight() == 0) - return false; + if (picture.getWidth() == 0 || picture.getHeight() == 0) return false; - Bitmap bitmap = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), - Config.ARGB_8888); + Bitmap bitmap = + Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), Config.ARGB_8888); picture.draw(new Canvas(bitmap)); - for (int i = 0; i < bitmap.getWidth(); i ++) { - for (int j = 0; j < bitmap.getHeight(); j ++) { + for (int i = 0; i < bitmap.getWidth(); i++) { + for (int j = 0; j < bitmap.getHeight(); j++) { if (color != bitmap.getPixel(i, j)) { return false; } @@ -2788,14 +2810,13 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } /** - * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started, - * scrolling is checked every SCROLL_WAIT_INTERVAL_MS for changes. Once - * changes have stopped, the function exits. If no scrolling has happened - * then the function exits after MIN_SCROLL_WAIT milliseconds. + * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started, scrolling is checked + * every SCROLL_WAIT_INTERVAL_MS for changes. Once changes have stopped, the function exits. If + * no scrolling has happened then the function exits after MIN_SCROLL_WAIT milliseconds. + * * @param previousScrollY The Y scroll position prior to waiting. */ - private void waitForScrollingComplete(int previousScrollY) - throws InterruptedException { + private void waitForScrollingComplete(int previousScrollY) throws InterruptedException { int scrollY = previousScrollY; // wait at least MIN_SCROLL_WAIT for something to happen. long noChangeMinWait = SystemClock.uptimeMillis() + MIN_SCROLL_WAIT_MS; @@ -2817,11 +2838,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi * androidx.webkit.WebViewCompatTest#testGetSafeBrowsingPrivacyPolicyUrl. Modifications to this * test should be reflected in that test as necessary. See http://go/modifying-webview-cts. */ + @Test public void testGetSafeBrowsingPrivacyPolicyUrl() throws Exception { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - assertNotNull(WebView.getSafeBrowsingPrivacyPolicyUrl()); try { new URL(WebView.getSafeBrowsingPrivacyPolicyUrl().toString()); @@ -2830,11 +2848,8 @@ public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActi } } + @Test public void testWebViewClassLoaderReturnsNonNull() { - if (!NullWebViewUtils.isWebViewAvailable()) { - return; - } - assertNotNull(WebView.getWebViewClassLoader()); } } diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java index fad09f465bf..c0e5624b000 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java @@ -291,10 +291,8 @@ public class ConcurrencyTest extends WifiJUnit3TestBase { || state == NetworkInfo.DetailedState.DISCONNECTED) { state = waitForNextNetworkState(); } - if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) { - if (state != NetworkInfo.DetailedState.CONNECTING) { - return false; - } + if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU) + && state == NetworkInfo.DetailedState.CONNECTING) { state = waitForNextNetworkState(); } return state == NetworkInfo.DetailedState.CONNECTED; diff --git a/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java b/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java index 6205ee8806f..41cc640b30d 100644 --- a/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java +++ b/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java @@ -562,7 +562,7 @@ public class WifiRttTest extends TestBase { */ @Test public void testAwareRttWithPeerHandle() throws InterruptedException { - if (WifiFeature.isAwareSupported(getContext())) { + if (!WifiFeature.isAwareSupported(getContext())) { return; } PeerHandle peerHandle = mock(PeerHandle.class); diff --git a/tests/uwb/Android.bp b/tests/uwb/Android.bp index 94a4f506602..55e7ba628d2 100644 --- a/tests/uwb/Android.bp +++ b/tests/uwb/Android.bp @@ -36,6 +36,7 @@ android_test { "mockito-target-minus-junit4", "com.uwb.support.fira", "com.uwb.support.multichip", + "com.uwb.support.oemextension", ], srcs: ["src/**/*.java"], platform_apis: true, diff --git a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java index 097f799dbff..f10eb11d018 100644 --- a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java +++ b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java @@ -26,10 +26,13 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.UiAutomation; @@ -53,16 +56,24 @@ import androidx.test.filters.SmallTest; import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.ShellIdentityUtils; +import com.android.modules.utils.build.SdkLevel; +import com.google.uwb.support.fira.FiraControleeParams; import com.google.uwb.support.fira.FiraOpenSessionParams; import com.google.uwb.support.fira.FiraParams; import com.google.uwb.support.fira.FiraProtocolVersion; +import com.google.uwb.support.fira.FiraSpecificationParams; import com.google.uwb.support.multichip.ChipInfoParams; +import com.google.uwb.support.oemextension.DeviceStatus; +import com.google.uwb.support.oemextension.RangingReportMetadata; +import com.google.uwb.support.oemextension.SessionStatus; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.EnumSet; import java.util.List; import java.util.Random; import java.util.Set; @@ -82,6 +93,9 @@ public class UwbManagerTest { private final Context mContext = InstrumentationRegistry.getContext(); private UwbManager mUwbManager; private String mDefaultChipId; + public static final int UWB_SESSION_STATE_IDLE = 0x03; + public static final byte DEVICE_STATE_ACTIVE = 0x02; + public static final int REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS = 0x00; @Before public void setup() throws Exception { @@ -494,6 +508,16 @@ public class UwbManagerTest { /* pass */ Log.i(TAG, "Failed with expected security exception: " + e); } + try { + uiAutomation.adoptShellPermissionIdentity(); + mUwbManager.unregisterUwbVendorUciCallback(cb); + /* pass */ + } catch (SecurityException e) { + /* fail */ + fail(); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } } @Test @@ -525,6 +549,8 @@ public class UwbManagerTest { public boolean onOpenFailedCalled; public boolean onStartedCalled; public boolean onStartFailedCalled; + public boolean onReconfiguredCalled; + public boolean onReconfiguredFailedCalled; public boolean onClosedCalled; public RangingSession rangingSession; public RangingReport rangingReport; @@ -566,9 +592,15 @@ public class UwbManagerTest { mCtrlCountDownLatch.countDown(); } - public void onReconfigured(@NonNull PersistableBundle params) { } + public void onReconfigured(@NonNull PersistableBundle params) { + onReconfiguredCalled = true; + mCtrlCountDownLatch.countDown(); + } - public void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params) { } + public void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params) { + onReconfiguredFailedCalled = true; + mCtrlCountDownLatch.countDown(); + } public void onStopped(@Reason int reason, @NonNull PersistableBundle parameters) { } @@ -902,6 +934,107 @@ public class UwbManagerTest { } } + @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) + public void testFiraRangingSessionWithProvisionedSTS() throws Exception { + UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); + // Needs UWB_PRIVILEGED permission which is held by shell. + uiAutomation.adoptShellPermissionIdentity(); + CancellationSignal cancellationSignal = null; + CountDownLatch countDownLatch = new CountDownLatch(1); + CountDownLatch resultCountDownLatch = new CountDownLatch(1); + RangingSessionCallback rangingSessionCallback = + new RangingSessionCallback(countDownLatch, resultCountDownLatch); + PersistableBundle bundle = mUwbManager.getSpecificationInfo(); + if (bundle.keySet().contains(FiraParams.PROTOCOL_NAME)) { + bundle = requireNonNull(bundle.getPersistableBundle(FiraParams.PROTOCOL_NAME)); + } + FiraSpecificationParams params = + FiraSpecificationParams.fromBundle(bundle); + EnumSet<FiraParams.StsCapabilityFlag> stsCapabilities = EnumSet.of( + FiraParams.StsCapabilityFlag.HAS_STATIC_STS_SUPPORT, + FiraParams.StsCapabilityFlag.HAS_PROVISIONED_STS_SUPPORT); + assumeTrue(params.getStsCapabilities() == stsCapabilities); + + FiraOpenSessionParams firaOpenSessionParams = new FiraOpenSessionParams.Builder() + .setProtocolVersion(new FiraProtocolVersion(1, 1)) + .setSessionId(1) + .setStsConfig(FiraParams.STS_CONFIG_PROVISIONED) + .setSessionKey(new byte[]{ + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 + }) + .setSubsessionKey(new byte[]{ + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 + }) + .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER) + .setDeviceRole(FiraParams.RANGING_DEVICE_ROLE_INITIATOR) + .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST) + .setDeviceAddress(UwbAddress.fromBytes(new byte[] {0x5, 6})) + .setDestAddressList(List.of(UwbAddress.fromBytes(new byte[] {0x5, 6}))) + .build(); + try { + // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell. + uiAutomation.adoptShellPermissionIdentity(); + cancellationSignal = mUwbManager.openRangingSession( + firaOpenSessionParams.toBundle(), + Executors.newSingleThreadExecutor(), + rangingSessionCallback, + mDefaultChipId); + // Wait for the on opened callback. + assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(rangingSessionCallback.onOpenedCalled).isTrue(); + assertThat(rangingSessionCallback.onOpenFailedCalled).isFalse(); + assertThat(rangingSessionCallback.rangingSession).isNotNull(); + + countDownLatch = new CountDownLatch(1); + rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch); + rangingSessionCallback.rangingSession.start(new PersistableBundle()); + // Wait for the on started callback. + assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(rangingSessionCallback.onStartedCalled).isTrue(); + assertThat(rangingSessionCallback.onStartFailedCalled).isFalse(); + + countDownLatch = new CountDownLatch(1); + rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch); + UwbAddress uwbAddress = UwbAddress.fromBytes(new byte[] {0x5, 5}); + rangingSessionCallback.rangingSession.addControlee( + new FiraControleeParams.Builder() + .setAddressList(new UwbAddress[] {uwbAddress}) + .setSubSessionIdList(new int[] {1}) + .build().toBundle() + ); + // Wait for the on reconfigured callback. + assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(rangingSessionCallback.onReconfiguredCalled).isTrue(); + assertThat(rangingSessionCallback.onReconfiguredFailedCalled).isFalse(); + + // Wait for the on ranging report callback. + assertThat(resultCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(rangingSessionCallback.rangingReport).isNotNull(); + + // Check the UWB state. + assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_ACTIVE); + + // Stop ongoing session. + rangingSessionCallback.rangingSession.stop(); + } finally { + if (cancellationSignal != null) { + countDownLatch = new CountDownLatch(1); + rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch); + + // Close session. + cancellationSignal.cancel(); + + // Wait for the on closed callback. + assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(rangingSessionCallback.onClosedCalled).isTrue(); + } + uiAutomation.dropShellPermissionIdentity(); + } + } + private class AdapterStateCallback implements UwbManager.AdapterStateCallback { private final CountDownLatch mCountDownLatch; private final @State Integer mWaitForState; @@ -1011,4 +1144,207 @@ public class UwbManagerTest { uiAutomation.dropShellPermissionIdentity(); } } + + private class UwbOemExtensionCallback implements UwbManager.UwbOemExtensionCallback { + public PersistableBundle mSessionChangeNtf; + public PersistableBundle mDeviceStatusNtf; + public PersistableBundle mSessionConfig; + public RangingReport mRangingReport; + public boolean onSessionConfigCompleteCalled = false; + public boolean onRangingReportReceivedCalled = false; + public boolean onSessionChangedCalled = false; + public boolean onDeviceStatusNtfCalled = false; + + @Override + public void onSessionStatusNotificationReceived( + @NonNull PersistableBundle sessionStatusBundle) { + mSessionChangeNtf = sessionStatusBundle; + onSessionChangedCalled = true; + } + + @Override + public void onDeviceStatusNotificationReceived(PersistableBundle deviceStatusBundle) { + mDeviceStatusNtf = deviceStatusBundle; + onDeviceStatusNtfCalled = true; + } + + @NonNull + @Override + public int onSessionConfigurationComplete(@NonNull PersistableBundle openSessionBundle) { + mSessionConfig = openSessionBundle; + onSessionConfigCompleteCalled = true; + return 0; + } + + @NonNull + @Override + public RangingReport onRangingReportReceived( + @NonNull RangingReport rangingReport) { + onRangingReportReceivedCalled = true; + mRangingReport = rangingReport; + return mRangingReport; + } + } + + @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) + public void testOemCallbackExtension() throws Exception { + Assume.assumeTrue(SdkLevel.isAtLeastU()); + UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); + CancellationSignal cancellationSignal = null; + CountDownLatch countDownLatch = new CountDownLatch(1); + CountDownLatch resultCountDownLatch = new CountDownLatch(1); + UwbOemExtensionCallback uwbOemExtensionCallback = new UwbOemExtensionCallback(); + + int sessionId = 1; + RangingSessionCallback rangingSessionCallback = + new RangingSessionCallback(countDownLatch, resultCountDownLatch); + FiraOpenSessionParams firaOpenSessionParams = new FiraOpenSessionParams.Builder() + .setProtocolVersion(new FiraProtocolVersion(1, 1)) + .setSessionId(sessionId) + .setStsConfig(FiraParams.STS_CONFIG_STATIC) + .setVendorId(new byte[]{0x5, 0x6}) + .setStaticStsIV(new byte[]{0x5, 0x6, 0x9, 0xa, 0x4, 0x6}) + .setDeviceType(FiraParams.RANGING_DEVICE_TYPE_CONTROLLER) + .setDeviceRole(FiraParams.RANGING_DEVICE_ROLE_INITIATOR) + .setMultiNodeMode(FiraParams.MULTI_NODE_MODE_UNICAST) + .setDeviceAddress(UwbAddress.fromBytes(new byte[] {0x5, 6})) + .setDestAddressList(List.of(UwbAddress.fromBytes(new byte[] {0x5, 6}))) + .build(); + try { + // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell. + uiAutomation.adoptShellPermissionIdentity(); + mUwbManager.registerUwbOemExtensionCallback( + Executors.newSingleThreadExecutor(), uwbOemExtensionCallback); + // Try to start a ranging session with invalid params, should fail. + cancellationSignal = mUwbManager.openRangingSession( + firaOpenSessionParams.toBundle(), + Executors.newSingleThreadExecutor(), + rangingSessionCallback, + mDefaultChipId); + // Wait for the on opened callback. + assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(uwbOemExtensionCallback.onSessionConfigCompleteCalled).isTrue(); + assertThat(uwbOemExtensionCallback.mSessionConfig).isNotNull(); + + FiraOpenSessionParams openSessionParamsBundle = FiraOpenSessionParams + .fromBundle(uwbOemExtensionCallback.mSessionConfig); + assertEquals(openSessionParamsBundle.getSessionId(), sessionId); + assertEquals(openSessionParamsBundle.getStsConfig(), FiraParams.STS_CONFIG_STATIC); + assertEquals(openSessionParamsBundle.getDeviceType(), + FiraParams.RANGING_DEVICE_TYPE_CONTROLLER); + + assertThat(uwbOemExtensionCallback.onSessionChangedCalled).isTrue(); + assertThat(uwbOemExtensionCallback.mSessionChangeNtf).isNotNull(); + + SessionStatus sessionStatusBundle = SessionStatus + .fromBundle(uwbOemExtensionCallback.mSessionChangeNtf); + assertEquals(sessionStatusBundle.getSessionId(), sessionId); + assertEquals(sessionStatusBundle.getState(), UWB_SESSION_STATE_IDLE); + assertEquals(sessionStatusBundle.getReasonCode(), + REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS); + + countDownLatch = new CountDownLatch(1); + rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch); + rangingSessionCallback.rangingSession.start(new PersistableBundle()); + // Wait for the on started callback. + assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(uwbOemExtensionCallback.onSessionChangedCalled).isTrue(); + assertThat(uwbOemExtensionCallback.mSessionChangeNtf).isNotNull(); + assertThat(uwbOemExtensionCallback.onDeviceStatusNtfCalled).isTrue(); + assertThat(uwbOemExtensionCallback.mDeviceStatusNtf).isNotNull(); + + DeviceStatus deviceStatusBundle = DeviceStatus + .fromBundle(uwbOemExtensionCallback.mDeviceStatusNtf); + assertEquals(deviceStatusBundle.getDeviceState(), DEVICE_STATE_ACTIVE); + + // Wait for the on ranging report callback. + assertThat(resultCountDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(rangingSessionCallback.rangingReport).isNotNull(); + assertThat(uwbOemExtensionCallback.onRangingReportReceivedCalled).isTrue(); + assertThat(uwbOemExtensionCallback.mRangingReport).isNotNull(); + PersistableBundle reportMetadataBundle = uwbOemExtensionCallback + .mRangingReport.getRangingReportMetadata(); + RangingReportMetadata reportMetadata = RangingReportMetadata + .fromBundle(reportMetadataBundle); + assertEquals(reportMetadata.getSessionId(), sessionId); + assertThat(reportMetadata.getRawNtfData()).isNotEmpty(); + + // Check the UWB state. + assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_ACTIVE); + + // Stop ongoing session. + rangingSessionCallback.rangingSession.stop(); + } finally { + if (cancellationSignal != null) { + countDownLatch = new CountDownLatch(1); + rangingSessionCallback.replaceCtrlCountDownLatch(countDownLatch); + + // Close session. + cancellationSignal.cancel(); + + // Wait for the on closed callback. + assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(rangingSessionCallback.onClosedCalled).isTrue(); + } + try { + mUwbManager.unregisterUwbOemExtensionCallback(uwbOemExtensionCallback); + } catch (SecurityException e) { + /* pass */ + fail(); + } + uiAutomation.dropShellPermissionIdentity(); + } + } + + @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) + public void testRegisterUwbOemExtensionCallbackWithoutUwbPrivileged() { + Assume.assumeTrue(SdkLevel.isAtLeastU()); + UwbManager.UwbOemExtensionCallback cb = new UwbOemExtensionCallback(); + try { + mUwbManager.registerUwbOemExtensionCallback( + Executors.newSingleThreadExecutor(), cb); + // should fail if the call was successful without UWB_PRIVILEGED permission. + fail(); + } catch (SecurityException e) { + /* pass */ + Log.i(TAG, "Failed with expected security exception: " + e); + } + } + + @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) + public void testUnregisterUwbOemExtensionCallbackWithoutUwbPrivileged() { + Assume.assumeTrue(SdkLevel.isAtLeastU()); + UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); + UwbManager.UwbOemExtensionCallback cb = new UwbOemExtensionCallback(); + try { + // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell. + uiAutomation.adoptShellPermissionIdentity(); + mUwbManager.registerUwbOemExtensionCallback( + Executors.newSingleThreadExecutor(), cb); + } catch (SecurityException e) { + /* fail */ + fail(); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } + try { + mUwbManager.unregisterUwbOemExtensionCallback(cb); + // should fail if the call was successful without UWB_PRIVILEGED permission. + fail(); + } catch (SecurityException e) { + /* pass */ + Log.i(TAG, "Failed with expected security exception: " + e); + } + try { + // Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell. + uiAutomation.adoptShellPermissionIdentity(); + mUwbManager.unregisterUwbOemExtensionCallback(cb); + } catch (SecurityException e) { + /* pass */ + fail(); + } + } } diff --git a/tests/uwb/src/android/uwb/cts/UwbTestUtils.java b/tests/uwb/src/android/uwb/cts/UwbTestUtils.java index 041b66c4337..a7689deec46 100644 --- a/tests/uwb/src/android/uwb/cts/UwbTestUtils.java +++ b/tests/uwb/src/android/uwb/cts/UwbTestUtils.java @@ -18,6 +18,7 @@ package android.uwb.cts; import android.content.Context; import android.content.pm.PackageManager; +import android.os.PersistableBundle; import android.os.SystemClock; import android.uwb.AngleMeasurement; import android.uwb.AngleOfArrivalMeasurement; @@ -63,6 +64,12 @@ public class UwbTestUtils { return getRangingMeasurement(getUwbAddress(false)); } + public static PersistableBundle getTestRangingMetadata() { + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt("TEST_KEY", 1); + return bundle; + } + public static RangingMeasurement getRangingMeasurement(UwbAddress address) { return new RangingMeasurement.Builder() .setDistanceMeasurement(getDistanceMeasurement()) @@ -74,6 +81,7 @@ public class UwbTestUtils { .setLineOfSight(RangingMeasurement.NLOS) .setMeasurementFocus(RangingMeasurement.MEASUREMENT_FOCUS_RANGE) .setRssiDbm(-85) + .setRangingMeasurementMetadata(getTestRangingMetadata()) .build(); } diff --git a/tools/cts-media-preparer-app/Android.bp b/tools/cts-media-preparer-app/Android.bp index cbad5684daa..7fe2a66ad1e 100644 --- a/tools/cts-media-preparer-app/Android.bp +++ b/tools/cts-media-preparer-app/Android.bp @@ -34,7 +34,7 @@ android_test { test_suites: [ "cts", "general-tests", - "mts", + "mts-media", ], sdk_version: "test_current", min_sdk_version: "29", |