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