1 /* 2 * Copyright (C) 2019 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.location.cts.fine; 18 19 import android.content.ContentResolver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.content.res.Resources; 24 import android.database.ContentObserver; 25 import android.net.wifi.WifiManager; 26 import android.os.Handler; 27 import android.os.HandlerThread; 28 import android.platform.test.annotations.AppModeFull; 29 import android.provider.Settings; 30 import android.test.AndroidTestCase; 31 32 import androidx.test.platform.app.InstrumentationRegistry; 33 import androidx.test.uiautomator.By; 34 import androidx.test.uiautomator.Direction; 35 import androidx.test.uiautomator.StaleObjectException; 36 import androidx.test.uiautomator.UiDevice; 37 import androidx.test.uiautomator.UiObject2; 38 import androidx.test.uiautomator.Until; 39 40 import com.android.compatibility.common.util.CddTest; 41 import com.android.compatibility.common.util.FeatureUtil; 42 import com.android.compatibility.common.util.PollingCheck; 43 44 import java.util.concurrent.CountDownLatch; 45 46 /** Tests if system settings app provides scanning settings. */ 47 @AppModeFull(reason = "Test cases don't apply for Instant apps") 48 public class ScanningSettingsTest extends AndroidTestCase { 49 private static final String TAG = "ScanningSettingsTest"; 50 51 private static final int TIMEOUT = 300_000; // 5 minutes 52 private static final int POLLING_INTERVAL_MILLIS = 1000; // 1 seconds. 53 private static final String SETTINGS_PACKAGE = "com.android.settings"; 54 55 private static final String LOCATION_SERVICES_PREFERENCE_TITLE_RES = 56 "location_services_preference_title"; 57 private static final String WIFI_SCANNING_TITLE_RES = 58 "location_scanning_wifi_always_scanning_title"; 59 private static final String BLUETOOTH_SCANNING_TITLE_RES = 60 "location_scanning_bluetooth_always_scanning_title"; 61 62 private UiDevice mDevice; 63 private Context mContext; 64 private String mLauncherPackage; 65 private PackageManager mPackageManager; 66 67 @Override setUp()68 protected void setUp() throws Exception { 69 super.setUp(); 70 // Can't use assumeTrue / assumeFalse because this is not a junit test, and so doesn't 71 // support using these keywords to trigger assumption failure and skip test. 72 if (FeatureUtil.isTV() || FeatureUtil.isAutomotive() || FeatureUtil.isWatch()) { 73 // TV, auto, and watch do not support the setting options of WIFI scanning and Bluetooth 74 // scanning 75 return; 76 } 77 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 78 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 79 80 mPackageManager = mContext.getPackageManager(); 81 final Intent launcherIntent = new Intent(Intent.ACTION_MAIN); 82 launcherIntent.addCategory(Intent.CATEGORY_HOME); 83 mLauncherPackage = 84 mPackageManager.resolveActivity(launcherIntent, PackageManager.MATCH_DEFAULT_ONLY) 85 .activityInfo 86 .packageName; 87 } 88 89 @CddTest(requirement = "7.4.2/C-2-1") testWifiScanningSettings()90 public void testWifiScanningSettings() throws Exception { 91 if (FeatureUtil.isTV() 92 || FeatureUtil.isAutomotive() 93 || FeatureUtil.isWatch() 94 || FeatureUtil.isArc()) { 95 return; 96 } 97 launchLocationServicesSettings(); 98 launchScanningSettingsFragment(WIFI_SCANNING_TITLE_RES); 99 100 final Resources res = mPackageManager.getResourcesForApplication(SETTINGS_PACKAGE); 101 final int resId = res.getIdentifier(WIFI_SCANNING_TITLE_RES, "string", SETTINGS_PACKAGE); 102 final UiObject2 pref = mDevice.findObject(By.text(res.getString(resId))); 103 104 final WifiManager wifiManager = mContext.getSystemService(WifiManager.class); 105 106 final boolean checked = wifiManager.isScanAlwaysAvailable(); 107 108 // Click the preference to toggle the setting. 109 pref.click(); 110 PollingCheck.check( 111 "Scan Always Available wasn't toggled from " + checked + " to " + !checked, 112 TIMEOUT, 113 () -> !checked == wifiManager.isScanAlwaysAvailable()); 114 115 // Click the preference again to toggle the setting back. 116 pref.click(); 117 PollingCheck.check( 118 "Scan Always Available wasn't toggled from " + !checked + " to " + checked, 119 TIMEOUT, 120 () -> checked == wifiManager.isScanAlwaysAvailable()); 121 } 122 123 @CddTest(requirement = "7.4.3/C-4-1") testBleScanningSettings()124 public void testBleScanningSettings() throws PackageManager.NameNotFoundException { 125 if (FeatureUtil.isTV() 126 || FeatureUtil.isAutomotive() 127 || FeatureUtil.isWatch() 128 || FeatureUtil.isArc()) { 129 return; 130 } 131 launchLocationServicesSettings(); 132 launchScanningSettingsFragment(BLUETOOTH_SCANNING_TITLE_RES); 133 134 toggleSettingAndVerify( 135 BLUETOOTH_SCANNING_TITLE_RES, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE); 136 } 137 launchLocationServicesSettings()138 private void launchLocationServicesSettings() { 139 final Intent intent = new Intent(Settings.ACTION_LOCATION_SCANNING_SETTINGS); 140 // Clear out any previous instances 141 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 142 mContext.startActivity(intent); 143 144 // Wait for the app to appear 145 mDevice.wait(Until.hasObject(By.pkg(SETTINGS_PACKAGE).depth(0)), TIMEOUT); 146 } 147 launchScanningSettingsFragment(String name)148 private void launchScanningSettingsFragment(String name) 149 throws PackageManager.NameNotFoundException { 150 final Resources res = mPackageManager.getResourcesForApplication(SETTINGS_PACKAGE); 151 int resIdMenu = res.getIdentifier(name, "string", SETTINGS_PACKAGE); 152 int resIdTitle = 153 res.getIdentifier( 154 LOCATION_SERVICES_PREFERENCE_TITLE_RES, "string", SETTINGS_PACKAGE); 155 UiObject2 pref = mDevice.findObject(By.text(res.getString(resIdMenu))); 156 while (pref == null) { 157 // Scroll to preference if the UI is scrollable 158 UiObject2 scrollable = 159 mDevice.findObject( 160 By.scrollable(true).hasDescendant(By.desc(res.getString(resIdTitle)))); 161 if (scrollable != null) { 162 try { 163 pref = 164 scrollable.scrollUntil( 165 Direction.DOWN, 166 Until.findObject(By.text(res.getString(resIdMenu)))); 167 } catch (StaleObjectException exception) { 168 // Ignore 169 } 170 } 171 if (pref == null) { 172 pref = mDevice.findObject(By.text(res.getString(resIdMenu))); 173 } 174 } 175 176 // Click the preference to show the Scanning fragment. The preference could be unclickable 177 // initially. So keep retrying until it's finally clicked. 178 // 179 // The following code: 180 // ``` 181 // pref.wait(Until.clickable(true), TIMEOUT); 182 // ``` 183 // couldn't detect the clickable state of the object unfortunately. We still need to do the 184 // check-and-retry hack. 185 do { 186 try { 187 pref.click(); 188 } catch (StaleObjectException exception) { 189 // Ignore 190 } 191 } while (!mDevice.wait( 192 Until.hasObject(By.res("com.android.settings:id/settingslib_main_switch_bar")), 193 POLLING_INTERVAL_MILLIS)); 194 } 195 clickAndWaitForSettingChange( UiObject2 pref, ContentResolver resolver, String settingKey)196 private void clickAndWaitForSettingChange( 197 UiObject2 pref, ContentResolver resolver, String settingKey) { 198 final CountDownLatch latch = new CountDownLatch(1); 199 final HandlerThread handlerThread = new HandlerThread(TAG); 200 handlerThread.start(); 201 final ContentObserver observer = 202 new ContentObserver(new Handler(handlerThread.getLooper())) { 203 @Override 204 public void onChange(boolean selfChange) { 205 super.onChange(selfChange); 206 latch.countDown(); 207 } 208 }; 209 resolver.registerContentObserver(Settings.Global.getUriFor(settingKey), false, observer); 210 pref.click(); 211 try { 212 latch.await(); 213 } catch (InterruptedException e) { 214 e.printStackTrace(); 215 } 216 handlerThread.quit(); 217 resolver.unregisterContentObserver(observer); 218 assertEquals(0, latch.getCount()); 219 } 220 toggleSettingAndVerify(String prefTitleRes, String settingKey)221 private void toggleSettingAndVerify(String prefTitleRes, String settingKey) 222 throws PackageManager.NameNotFoundException { 223 final Resources res = mPackageManager.getResourcesForApplication(SETTINGS_PACKAGE); 224 final int resId = res.getIdentifier(prefTitleRes, "string", SETTINGS_PACKAGE); 225 final UiObject2 pref = mDevice.findObject(By.text(res.getString(resId))); 226 final ContentResolver resolver = mContext.getContentResolver(); 227 final boolean checked = Settings.Global.getInt(resolver, settingKey, 0) == 1; 228 229 // Click the preference to toggle the setting. 230 clickAndWaitForSettingChange(pref, resolver, settingKey); 231 assertEquals(!checked, Settings.Global.getInt(resolver, settingKey, 0) == 1); 232 233 // Click the preference again to toggle the setting back. 234 clickAndWaitForSettingChange(pref, resolver, settingKey); 235 assertEquals(checked, Settings.Global.getInt(resolver, settingKey, 0) == 1); 236 } 237 } 238