1 /*
2  * Copyright (C) 2020 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.autofillservice.cts.inline;
18 
19 import static android.autofillservice.cts.testcore.CannedFillResponse.NO_RESPONSE;
20 import static android.autofillservice.cts.testcore.Helper.ID_PASSWORD;
21 import static android.autofillservice.cts.testcore.Helper.ID_USERNAME;
22 import static android.autofillservice.cts.testcore.Helper.assertTextIsSanitized;
23 import static android.autofillservice.cts.testcore.Helper.disablePccDetectionFeature;
24 import static android.autofillservice.cts.testcore.Helper.enablePccDetectionFeature;
25 import static android.autofillservice.cts.testcore.Helper.findAutofillIdByResourceId;
26 import static android.autofillservice.cts.testcore.Helper.findNodeByResourceId;
27 import static android.autofillservice.cts.testcore.Helper.getContext;
28 import static android.autofillservice.cts.testcore.Helper.isPccFieldClassificationSet;
29 import static android.autofillservice.cts.testcore.InstrumentedAutoFillServiceInlineEnabled.SERVICE_NAME;
30 import static android.autofillservice.cts.testcore.Timeouts.MOCK_IME_TIMEOUT_MS;
31 import static android.view.View.AUTOFILL_HINT_USERNAME;
32 
33 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
34 
35 import static com.google.common.truth.Truth.assertThat;
36 import static com.google.common.truth.Truth.assertWithMessage;
37 
38 import static org.junit.Assume.assumeTrue;
39 
40 import android.accessibilityservice.AccessibilityServiceInfo;
41 import android.app.PendingIntent;
42 import android.app.UiAutomation;
43 import android.autofillservice.cts.activities.DummyActivity;
44 import android.autofillservice.cts.activities.NonAutofillableActivity;
45 import android.autofillservice.cts.activities.UsernameOnlyActivity;
46 import android.autofillservice.cts.commontests.LoginActivityCommonTestCase;
47 import android.autofillservice.cts.testcore.CannedFillResponse;
48 import android.autofillservice.cts.testcore.Helper;
49 import android.autofillservice.cts.testcore.IdMode;
50 import android.autofillservice.cts.testcore.InlineUiBot;
51 import android.autofillservice.cts.testcore.InstrumentedAutoFillService;
52 import android.content.Intent;
53 import android.os.Binder;
54 import android.os.Bundle;
55 import android.os.SystemClock;
56 import android.platform.test.annotations.AppModeFull;
57 import android.platform.test.annotations.Presubmit;
58 import android.service.autofill.FillContext;
59 import android.util.Log;
60 import android.view.accessibility.AccessibilityManager;
61 
62 import androidx.test.platform.app.InstrumentationRegistry;
63 import androidx.test.uiautomator.Direction;
64 
65 import com.android.cts.mockime.ImeEventStream;
66 import com.android.cts.mockime.MockImeSession;
67 
68 import org.junit.After;
69 import org.junit.Ignore;
70 import org.junit.Test;
71 import org.junit.rules.TestRule;
72 
73 import java.util.concurrent.CountDownLatch;
74 import java.util.concurrent.TimeUnit;
75 
76 @Presubmit
77 public class InlineLoginActivityTest extends LoginActivityCommonTestCase {
78 
79     private static final String TAG = "InlineLoginActivityTest";
80 
81     @Override
enableService()82     protected void enableService() {
83         Helper.enableAutofillService(SERVICE_NAME);
84     }
85 
InlineLoginActivityTest()86     public InlineLoginActivityTest() {
87         super(getInlineUiBot());
88     }
89 
90     @Override
isInlineMode()91     protected boolean isInlineMode() {
92         return true;
93     }
94 
95     @Override
getMainTestRule()96     public TestRule getMainTestRule() {
97         return InlineUiBot.annotateRule(super.getMainTestRule());
98     }
99 
100     @After
disablePcc()101     public void disablePcc() {
102         Log.d(TAG, "@After: disablePcc()");
103         disablePccDetectionFeature(sContext);
104     }
105 
106     @Test
testAutofill_disjointDatasets()107     public void testAutofill_disjointDatasets() throws Exception {
108         // Set service.
109         enableService();
110 
111         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
112                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
113                         .setField(ID_USERNAME, "dude")
114                         .setPresentation(createPresentation("The Username"))
115                         .setInlinePresentation(createInlinePresentation("The Username"))
116                         .build())
117                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
118                         .setField(ID_PASSWORD, "sweet")
119                         .setPresentation(createPresentation("The Password"))
120                         .setInlinePresentation(createInlinePresentation("The Password"))
121                         .build())
122                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
123                         .setField(ID_PASSWORD, "lollipop")
124                         .setPresentation(createPresentation("The Password2"))
125                         .setInlinePresentation(createInlinePresentation("The Password2"))
126                         .build());
127 
128         sReplier.addResponse(builder.build());
129         mActivity.expectAutoFill("dude");
130 
131         // Trigger auto-fill.
132         mUiBot.selectByRelativeId(ID_USERNAME);
133         mUiBot.waitForIdleSync();
134 
135         mUiBot.assertDatasets("The Username");
136 
137         // Switch focus to password
138         mUiBot.selectByRelativeId(ID_PASSWORD);
139         mUiBot.waitForIdleSync();
140 
141         mUiBot.assertDatasets("The Password", "The Password2");
142 
143         // Switch focus back to username
144         mUiBot.selectByRelativeId(ID_USERNAME);
145         mUiBot.waitForIdleSync();
146 
147         mUiBot.assertDatasets("The Username");
148         mUiBot.selectDataset("The Username");
149         mUiBot.waitForIdleSync();
150 
151         // Check the results.
152         mActivity.assertAutoFilled();
153 
154         // Make sure input was sanitized.
155         final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
156         assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull();
157         assertTextIsSanitized(request.structure, ID_PASSWORD);
158         final FillContext fillContext = request.contexts.get(request.contexts.size() - 1);
159         assertThat(fillContext.getFocusedId())
160                 .isEqualTo(findAutofillIdByResourceId(fillContext, ID_USERNAME));
161 
162         // Make sure initial focus was properly set.
163         assertWithMessage("Username node is not focused").that(
164                 findNodeByResourceId(request.structure, ID_USERNAME).isFocused()).isTrue();
165         assertWithMessage("Password node is focused").that(
166                 findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse();
167     }
168 
169     @Test
testAutofill_SwitchToAutofillableActivity()170     public void testAutofill_SwitchToAutofillableActivity() throws Exception {
171         assertAutofill_SwitchActivity(UsernameOnlyActivity.class, /* autofillable */ true);
172     }
173 
174     @Test
testAutofill_SwitchToNonAutofillableActivity()175     public void testAutofill_SwitchToNonAutofillableActivity() throws Exception {
176         assertAutofill_SwitchActivity(NonAutofillableActivity.class, /* autofillable */ false);
177     }
178 
assertAutofill_SwitchActivity(Class<?> clazz, boolean autofillable)179     private void assertAutofill_SwitchActivity(Class<?> clazz, boolean autofillable)
180             throws Exception {
181         // Set service.
182         enableService();
183 
184         // Set expectations.
185         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
186                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
187                         .setField(ID_USERNAME, "dude")
188                         .setField(ID_PASSWORD, "password")
189                         .setPresentation(createPresentation("The Username"))
190                         .setInlinePresentation(createInlinePresentation("The Username"))
191                         .build());
192         sReplier.addResponse(builder.build());
193 
194         // Trigger auto-fill.
195         mUiBot.selectByRelativeId(ID_USERNAME);
196         mUiBot.waitForIdleSync();
197         sReplier.getNextFillRequest();
198         // Make sure the suggestion is shown.
199         mUiBot.assertDatasets("The Username");
200 
201         mUiBot.pressHome();
202         mUiBot.waitForIdle();
203 
204         // Switch to another Activity
205         startActivity(clazz);
206         mUiBot.waitForIdle();
207 
208         // Trigger input method show.
209         mUiBot.selectByRelativeId(ID_USERNAME);
210         mUiBot.waitForIdleSync();
211         if (autofillable) {
212             sReplier.addResponse(NO_RESPONSE);
213             sReplier.getNextFillRequest();
214         }
215         // Make sure suggestion is not shown.
216         mUiBot.assertNoDatasets();
217     }
218 
startActivity(Class<?> clazz)219     protected final void startActivity(Class<?> clazz) {
220         final Intent intent = new Intent(mContext, clazz);
221         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
222         mContext.startActivity(intent);
223     }
224 
225     @Test
testAutofill_selectDatasetThenHideInlineSuggestion()226     public void testAutofill_selectDatasetThenHideInlineSuggestion() throws Exception {
227         // Set service.
228         enableService();
229 
230         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
231                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
232                         .setField(ID_USERNAME, "dude")
233                         .setPresentation(createPresentation("The Username"))
234                         .setInlinePresentation(createInlinePresentation("The Username"))
235                         .build());
236 
237         sReplier.addResponse(builder.build());
238         mActivity.expectAutoFill("dude");
239 
240         // Trigger auto-fill.
241         mUiBot.selectByRelativeId(ID_USERNAME);
242         mUiBot.waitForIdleSync();
243 
244         mUiBot.assertDatasets("The Username");
245 
246         mUiBot.selectDataset("The Username");
247         mUiBot.waitForIdleSync();
248 
249         mUiBot.assertNoDatasets();
250 
251         // Make sure input was sanitized.
252         final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
253         assertWithMessage("CancelationSignal is null").that(request.cancellationSignal).isNotNull();
254         assertTextIsSanitized(request.structure, ID_PASSWORD);
255         final FillContext fillContext = request.contexts.get(request.contexts.size() - 1);
256         assertThat(fillContext.getFocusedId())
257                 .isEqualTo(findAutofillIdByResourceId(fillContext, ID_USERNAME));
258 
259         // Make sure initial focus was properly set.
260         assertWithMessage("Username node is not focused").that(
261                 findNodeByResourceId(request.structure, ID_USERNAME).isFocused()).isTrue();
262         assertWithMessage("Password node is focused").that(
263                 findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse();
264     }
265 
266     @Test
testLongClickAttribution()267     public void testLongClickAttribution() throws Exception {
268         // Set service.
269         enableService();
270 
271         Intent intent = new Intent(mContext, DummyActivity.class);
272         PendingIntent pendingIntent =
273                 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
274 
275         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
276                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
277                         .setField(ID_USERNAME, "dude")
278                         .setPresentation(createPresentation("The Username"))
279                         .setInlinePresentation(
280                                 createInlinePresentation("The Username", pendingIntent))
281                         .build());
282 
283         sReplier.addResponse(builder.build());
284         mActivity.expectAutoFill("dude");
285 
286         // Trigger auto-fill.
287         mUiBot.selectByRelativeId(ID_USERNAME);
288         mUiBot.waitForIdleSync();
289 
290         mUiBot.assertDatasets("The Username");
291 
292         // Long click on suggestion
293         mUiBot.longPressSuggestion("The Username");
294         mUiBot.waitForIdleSync();
295 
296         // Make sure the attribution showed worked
297         mUiBot.selectByText("foo");
298 
299         // Go back to the filled app.
300         mUiBot.pressBack();
301 
302         sReplier.getNextFillRequest();
303         mUiBot.waitForIdleSync();
304     }
305 
306     @Test
307     @AppModeFull(reason = "BROADCAST_STICKY permission cannot be granted to instant apps")
testAutofill_noInvalid()308     public void testAutofill_noInvalid() throws Exception {
309         final String keyInvalid = "invalid";
310         final String keyValid = "valid";
311         final String message = "Passes valid message to the remote service";
312         final Bundle bundle = new Bundle();
313         bundle.putBinder(keyInvalid, new Binder());
314         bundle.putString(keyValid, message);
315 
316         // Set service.
317         enableService();
318         final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
319         assumeTrue("MockIME not available", mockImeSession != null);
320 
321         mockImeSession.callSetInlineSuggestionsExtras(bundle);
322 
323         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
324                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
325                         .setField(ID_USERNAME, "dude")
326                         .setPresentation(createPresentation("The Username"))
327                         .setInlinePresentation(createInlinePresentation("The Username"))
328                         .build());
329 
330         sReplier.addResponse(builder.build());
331 
332         // Trigger auto-fill.
333         mUiBot.selectByRelativeId(ID_USERNAME);
334         mUiBot.waitForIdleSync();
335 
336         mUiBot.assertDatasets("The Username");
337 
338         final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
339         final Bundle extras = request.inlineRequest.getExtras();
340         assertThat(extras.get(keyInvalid)).isNull();
341         assertThat(extras.getString(keyValid)).isEqualTo(message);
342 
343         final Bundle style = request.inlineRequest.getInlinePresentationSpecs().get(0).getStyle();
344         assertThat(style.get(keyInvalid)).isNull();
345         assertThat(style.getString(keyValid)).isEqualTo(message);
346 
347         final Bundle style2 = request.inlineRequest.getInlinePresentationSpecs().get(1).getStyle();
348         assertThat(style2.get(keyInvalid)).isNull();
349         assertThat(style2.getString(keyValid)).isEqualTo(message);
350     }
351 
352     @Test
353     @AppModeFull(reason = "WRITE_SECURE_SETTING permission can't be grant to instant apps")
testSwitchInputMethod()354     public void testSwitchInputMethod() throws Exception {
355         // Set service
356         enableService();
357 
358         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
359                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
360                         .setField(ID_USERNAME, "dude")
361                         .setPresentation(createPresentation("The Username"))
362                         .setInlinePresentation(createInlinePresentation("The Username"))
363                         .build());
364 
365         sReplier.addResponse(builder.build());
366 
367         // Trigger auto-fill
368         mUiBot.selectByRelativeId(ID_USERNAME);
369         mUiBot.waitForIdleSync();
370 
371         mUiBot.assertDatasets("The Username");
372 
373         sReplier.getNextFillRequest();
374 
375         // Trigger IME switch event
376         Helper.mockSwitchInputMethod(sContext);
377         mUiBot.waitForIdleSync();
378 
379         final CannedFillResponse.Builder builder2 = new CannedFillResponse.Builder()
380                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
381                         .setField(ID_USERNAME, "dude2")
382                         .setPresentation(createPresentation("The Username 2"))
383                         .setInlinePresentation(createInlinePresentation("The Username 2"))
384                         .build());
385 
386         sReplier.addResponse(builder2.build());
387 
388         // Trigger auto-fill
389         mUiBot.selectByRelativeId(ID_USERNAME);
390         mUiBot.waitForIdleSync();
391 
392         // Confirm new suggestion
393         mUiBot.assertDatasets("The Username 2");
394 
395         // Confirm new fill request
396         sReplier.getNextFillRequest();
397     }
398 
399     @Test
400     @AppModeFull(reason = "BROADCAST_STICKY permission cannot be granted to instant apps")
testImeDisableInlineSuggestions_fallbackDropdownUi()401     public void testImeDisableInlineSuggestions_fallbackDropdownUi() throws Exception {
402         // Set service.
403         enableService();
404 
405         final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
406         assumeTrue("MockIME not available", mockImeSession != null);
407 
408         // Disable inline suggestions for the default service.
409         final Bundle bundle = new Bundle();
410         bundle.putBoolean("InlineSuggestions", false);
411         mockImeSession.callSetInlineSuggestionsExtras(bundle);
412 
413         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
414                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
415                         .setField(ID_USERNAME, "dude")
416                         .setPresentation(createPresentation("The Username"))
417                         .setInlinePresentation(createInlinePresentation("The Username"))
418                         .build());
419         sReplier.addResponse(builder.build());
420 
421         // Trigger auto-fill.
422         mUiBot.selectByRelativeId(ID_USERNAME);
423         mUiBot.waitForIdleSync();
424 
425         // Check that no inline requests are sent to the service.
426         final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
427         assertThat(request.inlineRequest).isNull();
428 
429         // Check dropdown UI shown.
430         getDropdownUiBot().assertDatasets("The Username");
431     }
432 
433     @Test
testTouchExplorationEnabledImeSupportInline_inlineShown()434     public void testTouchExplorationEnabledImeSupportInline_inlineShown() throws Exception {
435         enableTouchExploration();
436 
437         enableService();
438 
439         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
440                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
441                         .setField(ID_USERNAME, "dude")
442                         .setPresentation(createPresentation("The Username"))
443                         .setInlinePresentation(createInlinePresentation("The Username"))
444                         .build());
445         sReplier.addResponse(builder.build());
446 
447         try {
448             // Trigger auto-fill.
449             mUiBot.selectByRelativeId(ID_USERNAME);
450             mUiBot.waitForIdleSync();
451 
452             final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
453             assertThat(request.inlineRequest).isNotNull();
454 
455             // Check datasets shown.
456             mUiBot.assertDatasets("The Username");
457         } catch (Exception e) {
458             throw e;
459         } finally {
460             resetTouchExploration();
461         }
462     }
463 
464     @Test
testScrollSuggestionView()465     public void testScrollSuggestionView() throws Exception {
466         // Set service.
467         enableService();
468 
469         final int firstDataset = 1;
470         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder();
471         for (int i = firstDataset; i <= 20; i++) {
472             builder.addDataset(new CannedFillResponse.CannedDataset.Builder()
473                     .setField(ID_USERNAME, "dude" + i)
474                     .setPresentation(createPresentation("Username" + i))
475                     .setInlinePresentation(createInlinePresentation("Username" + i))
476                     .build());
477         }
478 
479         sReplier.addResponse(builder.build());
480 
481         // Trigger auto-fill.
482         mUiBot.selectByRelativeId(ID_USERNAME);
483         mUiBot.waitForIdleSync();
484 
485         mUiBot.assertSuggestion("Username" + firstDataset);
486 
487         // Scroll the suggestion view
488         mUiBot.scrollSuggestionView(Direction.RIGHT, /* speed */ 3000);
489         mUiBot.waitForIdleSync();
490 
491         mUiBot.assertNoSuggestion("Username" + firstDataset);
492 
493         sReplier.getNextFillRequest();
494         mUiBot.waitForIdleSync();
495     }
496 
497     @Test
testClickEventPassToIme()498     public void testClickEventPassToIme() throws Exception {
499         testTouchEventPassToIme(/* longPress */ false);
500     }
501 
502     @Test
testLongClickEventPassToIme()503     public void testLongClickEventPassToIme() throws Exception {
504         testTouchEventPassToIme(/* longPress */ true);
505     }
506 
testTouchEventPassToIme(boolean longPress)507     private void testTouchEventPassToIme(boolean longPress) throws Exception {
508         final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
509         assumeTrue("MockIME not available", mockImeSession != null);
510 
511         // Set service.
512         enableService();
513 
514         Intent intent = new Intent(mContext, DummyActivity.class);
515         PendingIntent pendingIntent =
516                 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
517 
518         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
519                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
520                         .setField(ID_USERNAME, "dude")
521                         .setPresentation(createPresentation("The Username"))
522                         .setInlinePresentation(longPress
523                                 ? createInlinePresentation("The Username", pendingIntent)
524                                 : createInlinePresentation("The Username"))
525                         .build());
526 
527         sReplier.addResponse(builder.build());
528 
529         final ImeEventStream stream = mockImeSession.openEventStream();
530 
531         // Trigger auto-fill.
532         mUiBot.selectByRelativeId(ID_USERNAME);
533         mUiBot.waitForIdleSync();
534         sReplier.getNextFillRequest();
535 
536         mUiBot.assertDatasets("The Username");
537 
538         if (longPress) {
539             // Long click on suggestion
540             mUiBot.longPressSuggestion("The Username");
541 
542             expectEvent(stream,
543                     event -> "onInlineSuggestionLongClickedEvent".equals(event.getEventName()),
544                     MOCK_IME_TIMEOUT_MS);
545         } else {
546             // Click on suggestion
547             mUiBot.selectDataset("The Username");
548 
549             expectEvent(stream,
550                     event -> "onInlineSuggestionClickedEvent".equals(event.getEventName()),
551                     MOCK_IME_TIMEOUT_MS);
552         }
553     }
554 
555     @Test
testInlineSuggestionViewReleased()556     public void testInlineSuggestionViewReleased() throws Exception {
557         // Set service
558         enableService();
559 
560         // Prepare the autofill response
561         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
562                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
563                         .setField(ID_USERNAME, "dude")
564                         .setPresentation(createPresentation("The Username"))
565                         .setInlinePresentation(createInlinePresentation("The Username"))
566                         .build())
567                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
568                         .setField(ID_PASSWORD, "sweet")
569                         .setPresentation(createPresentation("The Password"))
570                         .setInlinePresentation(createInlinePresentation("The Password"))
571                         .build())
572                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
573                         .setField(ID_PASSWORD, "lollipop")
574                         .setPresentation(createPresentation("The Password2"))
575                         .setInlinePresentation(createInlinePresentation("The Password2"))
576                         .build());
577         sReplier.addResponse(builder.build());
578 
579         // Trigger auto-fill on username field
580         mUiBot.selectByRelativeId(ID_USERNAME);
581         mUiBot.waitForIdleSync();
582         mUiBot.assertDatasets("The Username");
583         Helper.assertActiveViewCountFromInlineSuggestionRenderService(1);
584 
585         // Switch focus to password
586         mUiBot.selectByRelativeId(ID_PASSWORD);
587         mUiBot.waitForIdleSync();
588         mUiBot.assertDatasets("The Password", "The Password2");
589         Helper.assertActiveViewCountFromInlineSuggestionRenderService(2);
590 
591         // Switch focus back to username
592         mUiBot.selectByRelativeId(ID_USERNAME);
593         mUiBot.waitForIdleSync();
594         mUiBot.assertDatasets("The Username");
595         Helper.assertActiveViewCountFromInlineSuggestionRenderService(1);
596 
597         // Select the autofill suggestion on username, then check the results
598         mActivity.expectAutoFill("dude");
599         mUiBot.selectDataset("The Username");
600         mUiBot.waitForIdleSync();
601         mActivity.assertAutoFilled();
602         sReplier.getNextFillRequest();
603 
604         // Sleep for a while for the wait in {@link com.android.server.autofill.ui
605         // .RemoteInlineSuggestionUi} to timeout.
606         SystemClock.sleep(500);
607         Helper.assertActiveViewCountFromInlineSuggestionRenderService(0);
608     }
609 
610     @Test
611     @Ignore("b/281726966")
testAutofill_pccDatasets()612     public void testAutofill_pccDatasets() throws Exception {
613         // Set service.
614         enableService();
615         enablePccDetectionFeature(sContext, "username");
616         sReplier.setIdMode(IdMode.PCC_ID);
617 
618         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
619                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
620                         .setField(ID_USERNAME, "dude")
621                         .setField(ID_PASSWORD, "sweet")
622                         .setPresentation(createPresentation("The Dude"))
623                         .build())
624                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
625                         .setField(ID_USERNAME, "user1")
626                         .setField(ID_PASSWORD, "pass1")
627                         .setPresentation(createPresentation("generic user"))
628                         .build());
629         sReplier.addResponse(builder.build());
630 
631         // Trigger auto-fill.
632         mUiBot.selectByRelativeId(ID_USERNAME);
633         mUiBot.waitForIdleSync();
634 
635         final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
636         if (isPccFieldClassificationSet(sContext)) {
637             assertThat(request.hints.size()).isEqualTo(1);
638             assertThat(request.hints.get(0)).isEqualTo("username");
639         }
640         disablePccDetectionFeature(sContext);
641         sReplier.setIdMode(IdMode.RESOURCE_ID);
642     }
643 
644     @Test
645     @Ignore("b/281726966")
autofillPccDatasetTest_setForAllHints()646     public void autofillPccDatasetTest_setForAllHints() throws Exception {
647         // Set service.
648         enableService();
649         enablePccDetectionFeature(sContext, "username", "password", "new_password");
650         sReplier.setIdMode(IdMode.PCC_ID);
651 
652         final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
653                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
654                         .setField(AUTOFILL_HINT_USERNAME, "dude")
655                         .setField("allField1")
656                         .setPresentation(createPresentation("The Dude"))
657                         .build())
658                 .addDataset(new CannedFillResponse.CannedDataset.Builder()
659                         .setField("allField2")
660                         .setPresentation(createPresentation("generic user"))
661                         .build());
662         sReplier.addResponse(builder.build());
663 
664         // Trigger auto-fill.
665         mUiBot.selectByRelativeId(ID_USERNAME);
666         mUiBot.waitForIdleSync();
667 
668         final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
669         if (isPccFieldClassificationSet(sContext)) {
670             assertThat(request.hints.size()).isEqualTo(3);
671         }
672 
673         disablePccDetectionFeature(sContext);
674         sReplier.setIdMode(IdMode.RESOURCE_ID);
675     }
676 
enableTouchExploration()677     private void enableTouchExploration() throws InterruptedException {
678         toggleTouchExploration(/*enable=*/ true);
679     }
680 
resetTouchExploration()681     private void resetTouchExploration() throws InterruptedException {
682         toggleTouchExploration(/*enable=*/ false);
683     }
684 
toggleTouchExploration(boolean enable)685     private void toggleTouchExploration(boolean enable)
686             throws InterruptedException {
687         final AccessibilityManager manager =
688                 getContext().getSystemService(AccessibilityManager.class);
689         if (isTouchExplorationEnabled(manager) == enable) {
690             return;
691         }
692 
693         final CountDownLatch latch = new CountDownLatch(1);
694         AccessibilityManager.TouchExplorationStateChangeListener serviceListener =
695                 (boolean newState) -> {
696                     if (newState == enable) {
697                         latch.countDown();
698                     }
699                 };
700         manager.addTouchExplorationStateChangeListener(serviceListener);
701 
702         final UiAutomation uiAutomation =
703                 InstrumentationRegistry.getInstrumentation().getUiAutomation();
704         final AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
705         assert info != null;
706         if (enable) {
707             info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
708         } else {
709             info.flags &= ~AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
710         }
711         uiAutomation.setServiceInfo(info);
712 
713         // Wait for touch exploration state to be toggled
714         assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
715 
716         if (enable) {
717             assertThat(isTouchExplorationEnabled(manager)).isTrue();
718         } else {
719             assertThat(isTouchExplorationEnabled(manager)).isFalse();
720         }
721         manager.removeTouchExplorationStateChangeListener(serviceListener);
722     }
723 
isTouchExplorationEnabled(AccessibilityManager manager)724     private static boolean isTouchExplorationEnabled(AccessibilityManager manager) {
725         return manager.isEnabled() && manager.isTouchExplorationEnabled();
726     }
727 }
728