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 com.android.adservices.ui.settings;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.os.Build;
25 
26 import androidx.test.core.app.ApplicationProvider;
27 import androidx.test.ext.junit.runners.AndroidJUnit4;
28 import androidx.test.platform.app.InstrumentationRegistry;
29 import androidx.test.uiautomator.By;
30 import androidx.test.uiautomator.UiDevice;
31 import androidx.test.uiautomator.UiObject2;
32 import androidx.test.uiautomator.UiObjectNotFoundException;
33 import androidx.test.uiautomator.Until;
34 
35 import com.android.adservices.api.R;
36 import com.android.adservices.common.AdServicesFlagsSetterRule;
37 import com.android.adservices.common.AdservicesTestHelper;
38 import com.android.adservices.service.Flags;
39 import com.android.adservices.ui.util.ApkTestUtil;
40 import com.android.compatibility.common.util.ShellUtils;
41 import com.android.modules.utils.build.SdkLevel;
42 
43 import org.junit.After;
44 import org.junit.Assume;
45 import org.junit.Before;
46 import org.junit.Ignore;
47 import org.junit.Rule;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 
51 @RunWith(AndroidJUnit4.class)
52 public class AppConsentSettingsUiAutomatorTest {
53     private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
54     private static final String TEST_APP_NAME = "com.example.adservices.samples.ui.consenttestapp";
55     private static final String TEST_APP_APK_PATH =
56             "/data/local/tmp/cts/install/" + TEST_APP_NAME + ".apk";
57     private static final String TEST_APP_ACTIVITY_NAME = TEST_APP_NAME + ".MainActivity";
58     private static final ComponentName COMPONENT =
59             new ComponentName(TEST_APP_NAME, TEST_APP_ACTIVITY_NAME);
60 
61     private static final String PRIVACY_SANDBOX_PACKAGE = "android.adservices.ui.SETTINGS";
62     private static final String PRIVACY_SANDBOX_TEST_PACKAGE = "android.test.adservices.ui.MAIN";
63     private static final int LAUNCH_TIMEOUT = 5000;
64     private static UiDevice sDevice;
65 
66     private String mTestName;
67 
68     @Rule(order = 0)
69     public final AdServicesFlagsSetterRule flags =
70             AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests().setCompatModeFlags();
71 
72     @Before
setup()73     public void setup() throws UiObjectNotFoundException {
74         String installMessage = ShellUtils.runShellCommand("pm install -r " + TEST_APP_APK_PATH);
75         assertThat(installMessage).contains("Success");
76 
77         // Initialize UiDevice instance
78         sDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
79 
80         // Start from the home screen
81         sDevice.pressHome();
82     }
83 
84     @After
teardown()85     public void teardown() {
86         ApkTestUtil.takeScreenshot(sDevice, getClass().getSimpleName() + "_" + mTestName + "_");
87 
88         AdservicesTestHelper.killAdservicesProcess(CONTEXT);
89 
90         // Note aosp_x86 requires --user 0 to uninstall though arm doesn't.
91         ShellUtils.runShellCommand("pm uninstall --user 0 " + TEST_APP_NAME);
92     }
93 
94     // TODO: Remove this blank test along with the other @Ignore. b/268351419
95     @Test
placeholderTest()96     public void placeholderTest() {
97         mTestName = new Object() {}.getClass().getEnclosingMethod().getName();
98 
99         // As this class is the only test class in the test module and need to be @Ignore for the
100         // moment, add a blank test to help presubmit to pass.
101         assertThat(true).isTrue();
102     }
103 
104     @Test
105     @Ignore("Flaky test. (b/268351419)")
consentSystemServerOnlyTest()106     public void consentSystemServerOnlyTest() throws InterruptedException {
107         mTestName = new Object() {}.getClass().getEnclosingMethod().getName();
108 
109         // System server is not available on S-, skip this test for S-
110         Assume.assumeTrue(SdkLevel.isAtLeastT());
111         appConsentTest(0, false);
112     }
113 
114     @Test
115     @Ignore("Flaky test. (b/268351419)")
consentPpApiOnlyTest()116     public void consentPpApiOnlyTest() throws InterruptedException {
117         mTestName = new Object() {}.getClass().getEnclosingMethod().getName();
118 
119         appConsentTest(1, false);
120     }
121 
122     @Test
123     @Ignore("Flaky test. (b/268351419)")
consentSystemServerAndPpApiTest()124     public void consentSystemServerAndPpApiTest() throws InterruptedException {
125         mTestName = new Object() {}.getClass().getEnclosingMethod().getName();
126 
127         // System server is not available on S-, skip this test for S-
128         Assume.assumeTrue(SdkLevel.isAtLeastT());
129         appConsentTest(2, false);
130     }
131 
132     @Test
133     @Ignore("Flaky test. (b/268351419)")
consentAppSearchOnlyTest()134     public void consentAppSearchOnlyTest() throws UiObjectNotFoundException, InterruptedException {
135         ShellUtils.runShellCommand(
136                 "device_config put adservices enable_appsearch_consent_data true");
137         ShellUtils.runShellCommand("device_config put adservices consent_source_of_truth 3");
138         appConsentTest(Flags.APPSEARCH_ONLY, false);
139         ShellUtils.runShellCommand("device_config put adservices consent_source_of_truth null");
140     }
141 
142     @Test
143     @Ignore("Flaky test. (b/268351419)")
consentAppSearchOnlyDialogsOnTest()144     public void consentAppSearchOnlyDialogsOnTest()
145             throws UiObjectNotFoundException, InterruptedException {
146         ShellUtils.runShellCommand(
147                 "device_config put adservices enable_appsearch_consent_data true");
148         ShellUtils.runShellCommand("device_config put adservices consent_source_of_truth 3");
149         appConsentTest(Flags.APPSEARCH_ONLY, true);
150         ShellUtils.runShellCommand("device_config put adservices consent_source_of_truth null");
151     }
152 
153     @Test
154     @Ignore("Flaky test. (b/268351419)")
consentSystemServerOnlyDialogsOnTest()155     public void consentSystemServerOnlyDialogsOnTest()
156             throws UiObjectNotFoundException, InterruptedException {
157         mTestName = new Object() {}.getClass().getEnclosingMethod().getName();
158 
159         // System server is not available on S-, skip this test for S-
160         Assume.assumeTrue(SdkLevel.isAtLeastT());
161         appConsentTest(0, true);
162     }
163 
164     @Test
165     @Ignore("Flaky test. (b/268351419)")
consentPpApiOnlyDialogsOnTest()166     public void consentPpApiOnlyDialogsOnTest() throws InterruptedException {
167         mTestName = new Object() {}.getClass().getEnclosingMethod().getName();
168 
169         appConsentTest(1, true);
170     }
171 
172     @Test
173     @Ignore("Flaky test. (b/268351419)")
consentSystemServerAndPpApiDialogsOnTest()174     public void consentSystemServerAndPpApiDialogsOnTest() throws InterruptedException {
175         mTestName = new Object() {}.getClass().getEnclosingMethod().getName();
176 
177         // System server is not available on S-, skip this test for S-
178         Assume.assumeTrue(SdkLevel.isAtLeastT());
179         appConsentTest(2, true);
180     }
181 
setPpApiConsentToGiven()182     private void setPpApiConsentToGiven() {
183         // launch app
184         launchSettingApp();
185 
186         UiObject2 mainSwitch = ApkTestUtil.getConsentSwitch(sDevice);
187         assertThat(mainSwitch).isNotNull();
188 
189         if (!mainSwitch.isChecked()) {
190             mainSwitch.click();
191         }
192     }
193 
launchSettingApp()194     private void launchSettingApp() {
195         String privacySandboxUi;
196         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
197             privacySandboxUi = PRIVACY_SANDBOX_TEST_PACKAGE;
198         } else {
199             privacySandboxUi = PRIVACY_SANDBOX_PACKAGE;
200         }
201         Context context = ApplicationProvider.getApplicationContext();
202         Intent intent = new Intent(privacySandboxUi);
203         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
204         context.startActivity(intent);
205 
206         // Wait for the app to appear
207         sDevice.wait(Until.hasObject(By.pkg(privacySandboxUi).depth(0)), LAUNCH_TIMEOUT);
208     }
209 
appConsentTest(int consentSourceOfTruth, boolean dialogsOn)210     private void appConsentTest(int consentSourceOfTruth, boolean dialogsOn)
211             throws InterruptedException {
212         ShellUtils.runShellCommand(
213                 "device_config put adservices consent_source_of_truth " + consentSourceOfTruth);
214         ShellUtils.runShellCommand(
215                 "device_config put adservices ui_dialogs_feature_enabled " + dialogsOn);
216         AdservicesTestHelper.killAdservicesProcess(CONTEXT);
217 
218         // Wait for launcher
219         final String launcherPackage = sDevice.getLauncherPackageName();
220         assertThat(launcherPackage).isNotNull();
221         sDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT);
222         ShellUtils.runShellCommand("device_config put adservices ga_ux_enabled false");
223 
224         setPpApiConsentToGiven();
225 
226         // Initiate test app consent.
227         initiateTestAppConsent();
228 
229         // open apps view
230         launchSettingApp();
231         ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_title);
232 
233         blockAppConsent(dialogsOn);
234 
235         unblockAppConsent(dialogsOn);
236 
237         assertThat(ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_app_title))
238                 .isNotNull();
239 
240         resetAppConsent(dialogsOn);
241 
242         assertThat(ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_app_title, 0))
243                 .isNull();
244         assertThat(ApkTestUtil.getElement(sDevice, R.string.settingsUI_blocked_apps_title, 0))
245                 .isNull();
246         assertThat(ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_view_no_apps_text, 0))
247                 .isNotNull();
248     }
249 
unblockAppConsent(boolean dialogsOn)250     private void unblockAppConsent(boolean dialogsOn) throws InterruptedException {
251         ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_blocked_apps_title);
252         ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_unblock_app_title);
253 
254         if (dialogsOn) {
255             // click unblock
256             UiObject2 dialogTitle =
257                     ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_unblock_app_message);
258             UiObject2 positiveText =
259                     ApkTestUtil.getElement(
260                             sDevice, R.string.settingsUI_dialog_unblock_app_positive_text);
261             assertThat(dialogTitle).isNotNull();
262             assertThat(positiveText).isNotNull();
263 
264             // confirm
265             positiveText.click();
266         }
267 
268         assertThat(
269                         ApkTestUtil.getElement(
270                                 sDevice,
271                                 R.string.settingsUI_apps_view_no_blocked_apps_text))
272                 .isNotNull();
273         sDevice.pressBack();
274     }
275 
resetAppConsent(boolean dialogsOn)276     private void resetAppConsent(boolean dialogsOn) throws InterruptedException {
277         ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_apps_title);
278 
279         if (dialogsOn) {
280             UiObject2 dialogTitle =
281                     ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_app_message);
282             UiObject2 positiveText =
283                     ApkTestUtil.getElement(
284                             sDevice, R.string.settingsUI_dialog_reset_app_positive_text);
285             assertThat(dialogTitle).isNotNull();
286             assertThat(positiveText).isNotNull();
287 
288             // confirm
289             positiveText.click();
290         }
291     }
292 
blockAppConsent(boolean dialogsOn)293     private void blockAppConsent(boolean dialogsOn) throws InterruptedException {
294         ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_block_app_title);
295 
296         if (dialogsOn) {
297             UiObject2 dialogTitle =
298                     ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_app_message);
299             UiObject2 positiveText =
300                     ApkTestUtil.getElement(
301                             sDevice, R.string.settingsUI_dialog_block_app_positive_text);
302             assertThat(dialogTitle).isNotNull();
303             assertThat(positiveText).isNotNull();
304             positiveText.click();
305         }
306     }
307 
initiateTestAppConsent()308     private void initiateTestAppConsent() throws InterruptedException {
309         String installMessage = ShellUtils.runShellCommand("pm install -r " + TEST_APP_APK_PATH);
310         assertThat(installMessage).contains("Success");
311 
312         ShellUtils.runShellCommand("device_config set_sync_disabled_for_tests persistent");
313         ShellUtils.runShellCommand("device_config put adservices global_kill_switch false");
314         ShellUtils.runShellCommand(
315                 "device_config put adservices"
316                         + " fledge_custom_audience_service_kill_switch false");
317         ShellUtils.runShellCommand(
318                 "device_config put adservices"
319                         + " fledge_schedule_custom_audience_update_enabled true");
320         ShellUtils.runShellCommand(
321                 "device_config put adservices disable_fledge_enrollment_check true");
322 
323         ShellUtils.runShellCommand("device_config put adservices ppapi_app_allow_list *");
324 
325         Context context = ApplicationProvider.getApplicationContext();
326         Intent intent = new Intent().setComponent(COMPONENT);
327         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
328         context.startActivity(intent);
329 
330         sDevice.wait(Until.hasObject(By.pkg(TEST_APP_NAME).depth(0)), LAUNCH_TIMEOUT);
331 
332         Thread.sleep(1000);
333 
334         ShellUtils.runShellCommand("device_config set_sync_disabled_for_tests none");
335         ShellUtils.runShellCommand(
336                 "am force-stop com.example.adservices.samples.ui.consenttestapp");
337         ShellUtils.runShellCommand(
338                 "device_config put adservices disable_fledge_enrollment_check null");
339     }
340 }
341