CTS test for Android Security b/174046397

Bug: 174046397
Bug: 182917808
Test: Ran the new testcase on android-11.0.0_r34 with/without patch

Change-Id: I2e849088a3b57adcdae28ef7a1afac268ae18855
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java
new file mode 100644
index 0000000..45a1905
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0487.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0487 extends NonRootSecurityTestCase {
+    private static final String TEST_PKG = "android.security.cts.CVE_2021_0487";
+
+    /**
+     * b/174046397
+     * Vulnerable app    : CalendarProvider.apk
+     * Vulnerable module : com.android.providers.calendar
+     * Is Play managed   : No
+     */
+    @AsbSecurityTest(cveBugId = 174046397)
+    @Test
+    public void testPocCVE_2021_0487() {
+        ITestDevice device = null;
+        try {
+            device = getDevice();
+            /* Wake up the screen */
+            AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+            AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+            AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+            /* Install the application */
+            installPackage("CVE-2021-0487.apk");
+
+            AdbUtils.runCommandLine(
+                    "pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", device);
+            runDeviceTests(TEST_PKG, TEST_PKG + "." + "DeviceTest", "testOverlayButtonPresence");
+        } catch (Exception e) {
+            assumeNoException(e);
+        } finally {
+            try {
+                // return to home screen
+                AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+            } catch (Exception e) {
+                // ignore exceptions here
+            }
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp
new file mode 100644
index 0000000..14d6baf
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+    name: "CVE-2021-0487",
+    defaults: [
+        "cts_support_defaults",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "sts",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+    ],
+    sdk_version: "current",
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml
new file mode 100644
index 0000000..5c5934c
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.cts.CVE_2021_0487">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <application>
+        <service android:name=".PocService" />
+    </application>
+   <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.cts.CVE_2021_0487" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml
new file mode 100644
index 0000000..de5f253
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/integers.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <integer name="assumptionFailure">-1</integer>
+    <integer name="pass">0</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml
new file mode 100644
index 0000000..4d4098d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/res/values/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="broadcastReceiverCalendar">CalendarProviderBroadcastReceiver</string>
+    <string name="calendarClsName">%1$s.CalendarDebugActivity</string>
+    <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+    <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string>
+    <string name="dumpsysActivity">dumpsys activity %1$s</string>
+    <string name="errorMessage">Device is vulnerable to b/174046397 hence any app with
+    "SYSTEM_ALERT_WINDOW" can overlay the CalendarDebugActivity screen</string>
+    <string name="messageKey">message</string>
+    <string name="overlayButtonText">OverlayButton</string>
+    <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+    <string name="resultKey">result</string>
+    <string name="resumedTrue">mResumed=true</string>
+    <string name="sharedPreferences">CVE_2021_0487_prefs</string>
+    <string name="vulActivityNotRunningError">The CalendarDebugActivity is not currently
+    running on the device</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java
new file mode 100644
index 0000000..0a3e65d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/DeviceTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0487;
+
+import static android.provider.CalendarContract.ACTION_EVENT_REMINDER;
+import static android.provider.CalendarContract.CONTENT_URI;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+    private static final int LAUNCH_TIMEOUT_MS = 10000;
+    private String mVulnerablePkgName = "";
+    private Context mContext = getApplicationContext();
+    Resources mResources;
+
+    private void startOverlayService() {
+        Intent intent = new Intent(mContext, PocService.class);
+        assumeTrue(mContext.getString(R.string.canNotDrawOverlaysMsg),
+                Settings.canDrawOverlays(mContext));
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startService(intent);
+    }
+
+    private void startVulnerableActivity() {
+        Intent intent = new Intent(ACTION_EVENT_REMINDER);
+        Intent vulnPkgNameIntent = new Intent();
+        intent.setData(CONTENT_URI);
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> ris = pm.queryBroadcastReceivers(intent, 0);
+        for (ResolveInfo ri : ris) {
+            if (ri.activityInfo.name
+                    .contains(mContext.getString(R.string.broadcastReceiverCalendar))) {
+                mVulnerablePkgName = ri.activityInfo.packageName;
+            }
+        }
+        vulnPkgNameIntent.setClassName(mVulnerablePkgName,
+                mContext.getString(R.string.calendarClsName, mVulnerablePkgName));
+        vulnPkgNameIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(vulnPkgNameIntent);
+    }
+
+    @Test
+    public void testOverlayButtonPresence() {
+        try {
+            UiDevice device = UiDevice.getInstance(getInstrumentation());
+
+            /* Go to home screen */
+            device.pressHome();
+
+            /* Start the overlay service */
+            startOverlayService();
+            mResources = mContext.getResources();
+
+            SharedPreferences sharedPrefs = mContext.getSharedPreferences(
+                    mResources.getString(R.string.sharedPreferences), Context.MODE_APPEND);
+            Semaphore preferenceChanged = new Semaphore(0);
+            OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
+                @Override
+                public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+                        String key) {
+                    if (key.equals(mResources.getString(R.string.resultKey))) {
+                        preferenceChanged.release();
+                    }
+                }
+            };
+            sharedPrefs.registerOnSharedPreferenceChangeListener(listener);
+
+            /* Wait for the overlay service to set some result in shared preferences */
+            assumeTrue(preferenceChanged.tryAcquire(LAUNCH_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+            int result = sharedPrefs.getInt(mResources.getString(R.string.resultKey),
+                    mResources.getInteger(R.integer.assumptionFailure));
+            String message = sharedPrefs.getString(mResources.getString(R.string.messageKey),
+                    mResources.getString(R.string.defaultSemaphoreMsg));
+            assumeTrue(message, result != mResources.getInteger(R.integer.assumptionFailure));
+
+            /* Wait for the overlay window */
+            Pattern overlayTextPattern = Pattern.compile(
+                    mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE);
+            assumeTrue(mContext.getString(R.string.overlayUiScreenError),
+                    device.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS));
+
+            /* Start the vulnerable activity */
+            startVulnerableActivity();
+
+            /* Wait until the object of overlay window is gone */
+            boolean overlayDisallowed =
+                    device.wait(Until.gone(By.pkg(mContext.getPackageName())), LAUNCH_TIMEOUT_MS);
+
+            /* Check if the currently running activity is the vulnerable activity */
+            String activityDump = device.executeShellCommand(
+                    mContext.getString(R.string.dumpsysActivity, mVulnerablePkgName));
+            Pattern activityPattern = Pattern.compile(mContext.getString(R.string.resumedTrue),
+                    Pattern.CASE_INSENSITIVE);
+            assumeTrue(mContext.getString(R.string.vulActivityNotRunningError),
+                    activityPattern.matcher(activityDump).find());
+
+            /* Failing the test as fix is not present */
+            assertTrue(mContext.getString(R.string.errorMessage), overlayDisallowed);
+        } catch (Exception e) {
+            assumeNoException(e);
+        }
+    }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java
new file mode 100644
index 0000000..1a8cd0f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0487/src/android/security/cts/CVE_2021_0487/PocService.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_0487;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import androidx.annotation.IntegerRes;
+
+public class PocService extends Service {
+    private Button mButton;
+    private WindowManager mWindowManager;
+    private WindowManager.LayoutParams mLayoutParams;
+
+    int getInteger(@IntegerRes int resId) {
+        return getResources().getInteger(resId);
+    }
+
+    private static int getScreenWidth() {
+        return Resources.getSystem().getDisplayMetrics().widthPixels;
+    }
+
+    private static int getScreenHeight() {
+        return Resources.getSystem().getDisplayMetrics().heightPixels;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        try {
+            mWindowManager = getSystemService(WindowManager.class);
+            mLayoutParams = new WindowManager.LayoutParams();
+            mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+            mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+            mLayoutParams.format = PixelFormat.OPAQUE;
+            mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+            mLayoutParams.width = getScreenWidth();
+            mLayoutParams.height = getScreenHeight();
+            mLayoutParams.x = getScreenWidth() / 2;
+            mLayoutParams.y = getScreenHeight() / 2;
+        } catch (Exception e) {
+            sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage());
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        try {
+            showFloatingWindow();
+            sendTestResult(getInteger(R.integer.pass), "");
+        } catch (Exception e) {
+            sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage());
+        }
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    @Override
+    public void onDestroy() {
+        try {
+            mWindowManager.removeView(mButton);
+            super.onDestroy();
+        } catch (Exception e) {
+            // ignore the exception
+        }
+    }
+
+    private void showFloatingWindow() {
+        mButton = new Button(this);
+        mButton.setText(getString(R.string.overlayButtonText));
+        mWindowManager.addView(mButton, mLayoutParams);
+        mButton.setTag(mButton.getVisibility());
+    }
+
+    private void sendTestResult(int result, String message) {
+        try {
+            SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences),
+                    Context.MODE_PRIVATE);
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey), result);
+            edit.putString(getString(R.string.messageKey), message);
+            edit.commit();
+        } catch (Exception e) {
+            // ignore the exception
+        }
+    }
+}