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