Updated CTS test for Android Security b/194695497

Bug: 194695497
Bug: 206513471
Test: Ran the new testcase on android-12.0.0_r1 with/without patch

Change-Id: I0c761f90717188c623a4fbc492e0d1a130c18853
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp
index d3e2302..2f87b9c 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp
@@ -30,10 +30,10 @@
     test_suites: [
         "sts",
     ],
-    sdk_version: "current",
     static_libs: [
         "androidx.test.core",
         "androidx.test.rules",
         "androidx.test.uiautomator_uiautomator",
     ],
+    platform_apis: true,
 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml
index f097825..74e263c 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml
@@ -22,12 +22,8 @@
     <uses-permission android:name="android.permission.BLUETOOTH"/>
     <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
     <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
-    <application
-        android:testOnly="true"
-        android:label="CVE-2021-39626"
-        android:supportsRtl="true">
-        <activity
-            android:name=".PocActivity"
+    <application>
+        <activity android:name=".PocActivity"
             android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -35,7 +31,6 @@
             </intent-filter>
         </activity>
     </application>
-
     <instrumentation
         android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.security.cts.CVE_2021_39626" />
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/integers.xml
new file mode 100644
index 0000000..d5ae744
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/integers.xml
@@ -0,0 +1,23 @@
+<?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>
+    <integer name="enabled">1</integer>
+    <integer name="disabled">2</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/strings.xml
new file mode 100644
index 0000000..e6f53e7
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="allowButtonResName">android:id/button1</string>
+    <string name="btAction">btAction</string>
+    <string name="className">.Settings$ConnectedDeviceDashboardActivity</string>
+    <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string>
+    <string name="defaultSettingsPkg">com.android.settings</string>
+    <string name="failMessage">Vulnerable to b/194695497 !!</string>
+    <string name="messageKey">message</string>
+    <string name="resultKey">result</string>
+    <string name="sharedPreferences">CVE_2021_39626_prefs</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java
index cd24540..6bb8d16 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java
@@ -18,14 +18,15 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeNotNull;
 import static org.junit.Assume.assumeTrue;
 
 import android.bluetooth.BluetoothAdapter;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
 import android.provider.Settings;
 
 import androidx.test.InstrumentationRegistry;
@@ -34,69 +35,121 @@
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
 
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidJUnit4.class)
 public class DeviceTest {
-    private static final int TIMEOUT = 5000;
-    private static Context context;
+    static final int TIMEOUT = 10000;
+    boolean mBtState = false;
+    BluetoothAdapter mBtAdapter;
+    Context mContext;
+    OnSharedPreferenceChangeListener mListener;
+    Resources mResources;
+    SharedPreferences mSharedPrefs;
+    Semaphore mPreferenceChanged;
+    UiDevice mDevice;
 
-    private static String getSettingsPkgName() {
+    private String getSettingsPkgName() {
         Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
         ComponentName settingsComponent =
-                settingsIntent.resolveActivity(context.getPackageManager());
+                settingsIntent.resolveActivity(mContext.getPackageManager());
         String pkgName = settingsComponent != null ? settingsComponent.getPackageName()
-                : "com.android.settings";
-        assumeNotNull(pkgName);
+                : mContext.getString(R.string.defaultSettingsPkg);
         return pkgName;
     }
 
-    private void openApplication(String applicationName) {
-        Intent intent = context.getPackageManager().getLaunchIntentForPackage(applicationName);
-        assumeNotNull(intent);
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    @After
+    public void tearDown() {
         try {
-            context.startActivity(intent);
+            // Disable bluetooth if it was OFF before the test
+            if (!mBtState) {
+                Intent intent = new Intent(mContext, PocActivity.class);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                intent.putExtra(mContext.getString(R.string.btAction),
+                        BluetoothAdapter.ACTION_REQUEST_DISABLE);
+                mContext.startActivity(intent);
+            }
+            mPreferenceChanged = new Semaphore(0);
+            mPreferenceChanged.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS);
+            int result = mSharedPrefs.getInt(mResources.getString(R.string.resultKey),
+                    mResources.getInteger(R.integer.assumptionFailure));
+            String message = mSharedPrefs.getString(mResources.getString(R.string.messageKey),
+                    mResources.getString(R.string.defaultSemaphoreMsg));
+
+            // Go to home screen
+            mDevice.pressHome();
         } catch (Exception e) {
-            assumeNoException(e);
+            // ignore the exception
         }
     }
 
     @Test
     public void testBtDiscoverable() {
-        // Initialize UiDevice instance
-        UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        context = InstrumentationRegistry.getInstrumentation().getContext();
-        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
-        assumeNotNull(btAdapter);
+        try {
+            // Initialize UiDevice instance
+            mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+            mContext = InstrumentationRegistry.getInstrumentation().getContext();
+            mBtAdapter = BluetoothAdapter.getDefaultAdapter();
 
-        // Save the state of bluetooth adapter to reset after the test
-        boolean btState = btAdapter.isEnabled();
-        if (!btState) {
-            // If bluetooth is disabled, enable it and wait for adapter startup to complete
-            assumeTrue(btAdapter.enable());
-            try {
-                Thread.sleep(TIMEOUT);
-            } catch (Exception e) {
-                assumeNoException(e);
-            }
+            // Save the state of bluetooth adapter to reset after the test
+            mBtState = mBtAdapter.isEnabled();
+
+            // If bluetooth is disabled, enable it and wait for start activity to complete
+            Intent intent = new Intent(mContext, PocActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.putExtra(mContext.getString(R.string.btAction),
+                    BluetoothAdapter.ACTION_REQUEST_ENABLE);
+            mContext.startActivity(intent);
+            mResources = mContext.getResources();
+
+            mSharedPrefs = mContext.getSharedPreferences(
+                    mResources.getString(R.string.sharedPreferences), Context.MODE_APPEND);
+            mPreferenceChanged = new Semaphore(0);
+            mListener = new OnSharedPreferenceChangeListener() {
+                @Override
+                public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+                        String key) {
+                    if (key.equals(mResources.getString(R.string.resultKey))) {
+                        mPreferenceChanged.release();
+                    }
+                }
+            };
+            mSharedPrefs.registerOnSharedPreferenceChangeListener(mListener);
+            mPreferenceChanged.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS);
+
+            int result = mSharedPrefs.getInt(mResources.getString(R.string.resultKey),
+                    mResources.getInteger(R.integer.assumptionFailure));
+            String message = mSharedPrefs.getString(mResources.getString(R.string.messageKey),
+                    mResources.getString(R.string.defaultSemaphoreMsg));
+            assumeTrue(message, result != mResources.getInteger(R.integer.assumptionFailure));
+
+            // Checking if bluetooth is enabled. The test requires bluetooth to be enabled,
+            // assumption failing the test if it's not enabled
+            assumeTrue(mBtAdapter.isEnabled());
+
+            // Launch bluetooth settings which is supposed to set scan mode to
+            // SCAN_MODE_CONNECTABLE_DISCOVERABLE if vulnerability is active
+            intent = new Intent();
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            String settingsPkg = getSettingsPkgName();
+            intent.setClassName(settingsPkg, settingsPkg + mContext.getString(R.string.className));
+            mContext.startActivity(intent);
+
+            assumeTrue(mDevice.wait(Until.hasObject(By.pkg(settingsPkg)), TIMEOUT));
+
+            boolean isBtDiscoverable = false;
+            isBtDiscoverable =
+                    (mBtAdapter.getScanMode() == mBtAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+
+            // The test fails if bluetooth is made discoverable through PoC
+            assertFalse(mContext.getString(R.string.failMessage), isBtDiscoverable);
+        } catch (Exception e) {
+            assumeNoException(e);
         }
-        assumeTrue(btAdapter.isEnabled());
-
-        // Launch the PoC application and ensure that it launches bluetooth settings
-        openApplication(context.getPackageName());
-        assumeTrue(device.wait(Until.hasObject(By.pkg(getSettingsPkgName())), TIMEOUT));
-
-        boolean isBtDiscoverable =
-                (btAdapter.getScanMode() == btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
-
-        // Disable bluetooth if it was OFF before the test
-        if (!btState) {
-            btAdapter.disable();
-        }
-
-        // The test fails if bluetooth is made discoverable through PoC
-        assertFalse("Vulnerable to b/194695497 !!", isBtDiscoverable);
     }
 }
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java
index d4425ff..9a43cd1 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java
@@ -16,24 +16,88 @@
 
 package android.security.cts.CVE_2021_39626;
 
-import static org.junit.Assume.assumeNoException;
-
 import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.Bundle;
-import android.provider.Settings;
+
+import androidx.annotation.IntegerRes;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 public class PocActivity extends Activity {
+    private static final int TIMEOUT = 5000;
+    private static final int REQUEST_ENABLE_BT = 1;
+    private static final int REQUEST_DISABLE_BT = 2;
+
+    int getInteger(@IntegerRes int resId) {
+        return getResources().getInteger(resId);
+    }
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        Intent intent = new Intent();
-        intent.setAction(Settings.ACTION_BLUETOOTH_SETTINGS);
         try {
-            startActivity(intent);
+            String action = getIntent().getStringExtra(getString(R.string.btAction));
+            UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+            BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class);
+            BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
+            int code = REQUEST_ENABLE_BT;
+            if (action.equals(BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
+                code = REQUEST_DISABLE_BT;
+            }
+
+            if ((action.equals(BluetoothAdapter.ACTION_REQUEST_ENABLE)
+                    && !bluetoothAdapter.isEnabled())
+                    || (action.equals(BluetoothAdapter.ACTION_REQUEST_DISABLE)
+                            && bluetoothAdapter.isEnabled())) {
+                Intent enableBtIntent = new Intent(action);
+                startActivityForResult(enableBtIntent, code);
+
+                // Wait for the activity to appear and the allow button
+                device.wait(Until.hasObject(By.res(getString(R.string.allowButtonResName))),
+                        TIMEOUT);
+
+                // Click on the allow button
+                UiObject2 object =
+                        device.findObject(By.res(getString(R.string.allowButtonResName)));
+                object.click();
+            } else {
+                sendTestResult(getInteger(R.integer.pass), "");
+                finish();
+                return;
+            }
         } catch (Exception e) {
-            assumeNoException(e);
+            sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage());
+            return;
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_OK) {
+            finish();
+            sendTestResult(getInteger(R.integer.enabled), "");
+        } else if (requestCode == REQUEST_DISABLE_BT && resultCode == Activity.RESULT_OK) {
+            finish();
+            sendTestResult(getInteger(R.integer.disabled), "");
+        }
+    }
+
+    private void sendTestResult(int result, String message) {
+        SharedPreferences sh =
+                getSharedPreferences(getString(R.string.sharedPreferences), Context.MODE_PRIVATE);
+        if (sh != null) {
+            SharedPreferences.Editor edit = sh.edit();
+            edit.putInt(getString(R.string.resultKey), result);
+            edit.putString(getString(R.string.messageKey), message);
+            edit.commit();
         }
     }
 }