CTS test for Android Security b/143559931

Bug: 143559931
Bug: 202759784
Test: Ran the new testcase on android-10.0.0_r41 to test with/without patch

Change-Id: I4840c588088d96f9ddec27eebb6a6b1c733de48e
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp
new file mode 100644
index 0000000..aa9f71f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ *
+ */
+
+android_test_helper_app {
+    name: "CVE-2021-0954",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    test_suites: [
+        "cts",
+        "vts10",
+        "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-0954/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml
new file mode 100644
index 0000000..a7e0218
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<!--
+  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"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.security.cts.cve_2021_0954"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <application
+        android:allowBackup="true"
+        android:label="CVE_2021_0954"
+        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_0954" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java
new file mode 100644
index 0000000..6e36fb3
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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_0954;
+
+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;
+
+import android.content.Context;
+import android.content.Intent;
+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.Before;
+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 String TEST_PKG = "android.security.cts.cve_2021_0954";
+    private static final String TEST_VULNERABLE_PKG = "android";
+    private static final String TEST_VULNERABLE_ACTIVITY =
+            "com.android.internal.app.ResolverActivity";
+    private static final int LAUNCH_TIMEOUT_MS = 20000;
+    private static final String vulnerableActivityName = "ResolverActivity";
+    private UiDevice mDevice;
+    String activityDump = "";
+
+    private void startOverlayService() {
+        Context context = getApplicationContext();
+        assertNotNull(context);
+        Intent intent = new Intent(context, PocService.class);
+        assertNotNull(intent);
+
+        if (Settings.canDrawOverlays(getApplicationContext())) {
+            context.startService(intent);
+        } else {
+            try {
+                context.startService(intent);
+            } catch (Exception e) {
+                throw new RuntimeException("Unable to start the overlay service", e);
+            }
+        }
+    }
+
+    public void startVulnerableActivity() {
+        Context context = getApplicationContext();
+        Intent intent = new Intent();
+        intent.setClassName(TEST_VULNERABLE_PKG, TEST_VULNERABLE_ACTIVITY);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(intent);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mDevice = UiDevice.getInstance(getInstrumentation());
+
+        /* Start the vulnerable activity */
+        startVulnerableActivity();
+        if (!mDevice.wait(Until.hasObject(By.res("android:id/contentPanel")
+                .clazz("android.widget.ScrollView").pkg("android")), LAUNCH_TIMEOUT_MS)) {
+            return;
+        }
+
+        /* Start the overlay service */
+        startOverlayService();
+    }
+
+    @Test
+    public void testVulnerableActivityPresence() {
+        Pattern overlayTextPattern = Pattern.compile("OverlayButton", Pattern.CASE_INSENSITIVE);
+        if (!mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS)) {
+            return;
+        }
+
+        /*
+         * Check if the currently running activity is the vulnerable activity, if not abort the test
+         */
+        try {
+            activityDump = mDevice.executeShellCommand("dumpsys activity");
+        } catch (IOException e) {
+            throw new RuntimeException("Could not execute dumpsys activity command");
+        }
+        Pattern activityPattern =
+                Pattern.compile("mResumedActivity.*" + vulnerableActivityName + ".*\n");
+        if (!activityPattern.matcher(activityDump).find()) {
+            return;
+        }
+        String message = "Device is vulnerable to b/143559931 hence any app with "
+                + "SYSTEM_ALERT_WINDOW can overlay the ResolverActivity screen";
+        assertNull(message, mDevice.findObject(By.text(overlayTextPattern)));
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java
new file mode 100644
index 0000000..82b78a2
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java
@@ -0,0 +1,93 @@
+/*
+ * 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_0954;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+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() {
+        if (Settings.canDrawOverlays(this)) {
+            mButton = new Button(getApplicationContext());
+            mButton.setText("OverlayButton");
+            mWindowManager.addView(mButton, mLayoutParams);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    onDestroy();
+                }
+            }, 60000); // one minute
+            mButton.setTag(mButton.getVisibility());
+        }
+    }
+}