1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.security.cts.TestBluetoothDiscoverable; 18 19 import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; 20 import static android.provider.SettingsSlicesContract.AUTHORITY; 21 22 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 23 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assume.assumeNoException; 26 import static org.junit.Assume.assumeTrue; 27 28 import android.app.Instrumentation; 29 import android.app.KeyguardManager; 30 import android.app.UiAutomation; 31 import android.bluetooth.BluetoothAdapter; 32 import android.content.BroadcastReceiver; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.res.Resources; 38 import android.net.Uri; 39 import android.os.PowerManager; 40 import android.provider.Settings; 41 42 import androidx.test.runner.AndroidJUnit4; 43 import androidx.test.uiautomator.By; 44 import androidx.test.uiautomator.UiDevice; 45 import androidx.test.uiautomator.Until; 46 47 import org.junit.After; 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 52 import java.util.concurrent.Semaphore; 53 import java.util.concurrent.TimeUnit; 54 55 @RunWith(AndroidJUnit4.class) 56 public class DeviceTest { 57 private static Context sContext; 58 private BluetoothAdapter mBtAdapter; 59 private BroadcastReceiver mBroadcastReceiver; 60 private Instrumentation mInstrumentation; 61 private Resources mResources; 62 private Semaphore mBroadcastReceived; 63 private String mErrorMessage; 64 private UiAutomation mUiAutomation; 65 private UiDevice mDevice; 66 private boolean mBtState; 67 private int mStatusCode; 68 69 @Before setUp()70 public void setUp() { 71 try { 72 mInstrumentation = getInstrumentation(); 73 sContext = mInstrumentation.getTargetContext(); 74 mBroadcastReceived = new Semaphore(0); 75 mBtState = false; 76 mResources = sContext.getResources(); 77 mBtAdapter = BluetoothAdapter.getDefaultAdapter(); 78 mStatusCode = mResources.getInteger(R.integer.assumptionFailure); 79 mErrorMessage = ""; 80 81 // Register BroadcastReceiver to receive status from PocActivity 82 mBroadcastReceiver = new BroadcastReceiver() { 83 @Override 84 public void onReceive(Context context, Intent intent) { 85 try { 86 if (intent.getAction() 87 .equals(mResources.getString(R.string.broadcastAction))) { 88 mStatusCode = 89 intent.getIntExtra(mResources.getString(R.string.resultKey), 90 mResources.getInteger(R.integer.assumptionFailure)); 91 mErrorMessage = intent 92 .getStringExtra(mResources.getString(R.string.messageKey)); 93 mBroadcastReceived.release(); 94 } 95 } catch (Exception ignored) { 96 // Ignore exceptions here 97 } 98 } 99 }; 100 IntentFilter filter = new IntentFilter(); 101 filter.addAction(sContext.getString(R.string.broadcastAction)); 102 sContext.registerReceiver(mBroadcastReceiver, filter); 103 104 // Save the state of bluetooth adapter to reset after the test 105 mBtState = mBtAdapter.isEnabled(); 106 107 // Disable bluetooth if already enabled in 'SCAN_MODE_CONNECTABLE_DISCOVERABLE' mode 108 if (mBtAdapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) { 109 switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE); 110 } 111 112 // Enable bluetooth if in disabled state 113 switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_ENABLE); 114 115 // 'MODIFY_PHONE_STATE' permission is required to launch target Settings app activity 116 mUiAutomation = mInstrumentation.getUiAutomation(); 117 mUiAutomation 118 .adoptShellPermissionIdentity(android.Manifest.permission.MODIFY_PHONE_STATE); 119 } catch (Exception e) { 120 assumeNoException(e); 121 } 122 } 123 124 @After tearDown()125 public void tearDown() { 126 try { 127 mUiAutomation.dropShellPermissionIdentity(); 128 // Disable bluetooth if it was OFF before the test 129 if (!mBtState) { 130 switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE); 131 } 132 sContext.unregisterReceiver(mBroadcastReceiver); 133 } catch (Exception e) { 134 // Ignore exceptions here 135 } 136 } 137 138 @Test testConnectedDeviceDashboardFragment()139 public void testConnectedDeviceDashboardFragment() { 140 try { 141 // Check if device is unlocked 142 PowerManager powerManager = sContext.getSystemService(PowerManager.class); 143 KeyguardManager keyguardManager = sContext.getSystemService(KeyguardManager.class); 144 assumeTrue(sContext.getString(R.string.msgDeviceLocked), 145 powerManager.isInteractive() && !keyguardManager.isKeyguardLocked()); 146 147 // Check if bluetooth is enabled. The test requires bluetooth to be enabled 148 assumeTrue(mBtAdapter.isEnabled()); 149 150 // Check if bluetooth mode is not set to SCAN_MODE_CONNECTABLE_DISCOVERABLE 151 assumeTrue(mBtAdapter.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE); 152 153 // Launch bluetooth settings which is supposed to set scan mode to 154 // SCAN_MODE_CONNECTABLE_DISCOVERABLE if vulnerability is present 155 String settingsPkg = getSettingsPkgName(); 156 Intent intent = new Intent(); 157 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 158 intent.setData(Uri.parse( 159 sContext.getString(R.string.sliceConnectedDevicesDashboardUri, settingsPkg))); 160 intent.setClassName(settingsPkg, 161 sContext.getString(R.string.sliceDeepLinkSpringBoardClassName, settingsPkg)); 162 sContext.startActivity(intent); 163 164 // Wait until target activity from settings package is launched 165 mDevice = UiDevice.getInstance(mInstrumentation); 166 assumeTrue(mDevice.wait(Until.hasObject(By.pkg(settingsPkg)), 167 mResources.getInteger(R.integer.timeoutMs))); 168 169 // Test fails if bluetooth is made discoverable through PoC 170 boolean isBtDiscoverable = 171 (mBtAdapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE); 172 assertFalse(sContext.getString(R.string.msgFailConnectedDeviceDashboardFragment), 173 isBtDiscoverable); 174 } catch (Exception e) { 175 assumeNoException(e); 176 } 177 } 178 179 @Test testBluetoothDashboardFragment()180 public void testBluetoothDashboardFragment() { 181 try { 182 // Check if device is unlocked 183 PowerManager powerManager = sContext.getSystemService(PowerManager.class); 184 KeyguardManager keyguardManager = sContext.getSystemService(KeyguardManager.class); 185 assumeTrue(sContext.getString(R.string.msgDeviceLocked), 186 powerManager.isInteractive() && !keyguardManager.isKeyguardLocked()); 187 188 // Check if bluetooth is enabled. The test requires bluetooth to be enabled 189 assumeTrue(mBtAdapter.isEnabled()); 190 191 // Check if bluetooth mode is not set to SCAN_MODE_CONNECTABLE_DISCOVERABLE 192 assumeTrue(mBtAdapter.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE); 193 194 // Launch bluetooth settings which is supposed to set scan mode to 195 // SCAN_MODE_CONNECTABLE_DISCOVERABLE if vulnerability is present 196 String settingsPkg = getSettingsPkgName(); 197 Intent intent = new Intent(); 198 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 199 intent.setData(Uri.parse(sContext.getString(R.string.sliceBluetoothDashboardUri, 200 settingsPkg, AUTHORITY))); 201 sContext.startActivity(intent); 202 203 // Wait until target activity from settings package is launched 204 mDevice = UiDevice.getInstance(mInstrumentation); 205 assumeTrue(mDevice.wait(Until.hasObject(By.pkg(settingsPkg)), 206 mResources.getInteger(R.integer.timeoutMs))); 207 208 // Test fails if bluetooth is made discoverable through PoC 209 boolean isBtDiscoverable = 210 (mBtAdapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE); 211 assertFalse(sContext.getString(R.string.msgFailBluetoothDashboardFragment), 212 isBtDiscoverable); 213 } catch (Exception e) { 214 assumeNoException(e); 215 } 216 } 217 218 getSettingsPkgName()219 public static String getSettingsPkgName() { 220 // Retrieve settings package name dynamically 221 Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); 222 ComponentName settingsComponent = 223 settingsIntent.resolveActivity(sContext.getPackageManager()); 224 String pkgName = settingsComponent != null ? settingsComponent.getPackageName() 225 : sContext.getString(R.string.defaultSettingsPkg); 226 return pkgName; 227 } 228 switchBluetoothMode(String action)229 private void switchBluetoothMode(String action) throws Exception { 230 // Start PocActivity to switch bluetooth mode 231 Intent intent = new Intent(sContext, PocActivity.class); 232 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 233 intent.putExtra(sContext.getString(R.string.btAction), action); 234 sContext.startActivity(intent); 235 236 // Wait until bluetooth mode switch is completed successfully 237 assumeTrue(mBroadcastReceived.tryAcquire(mResources.getInteger(R.integer.timeoutMs), 238 TimeUnit.MILLISECONDS)); 239 assumeTrue(mErrorMessage, 240 mStatusCode != mResources.getInteger(R.integer.assumptionFailure)); 241 } 242 } 243