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.autofillservice.cts.testcore;
18 
19 import static android.autofillservice.cts.testcore.Timeouts.CONNECTION_TIMEOUT;
20 import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
21 
22 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
23 
24 import static com.google.common.truth.Truth.assertWithMessage;
25 
26 import android.app.Activity;
27 import android.app.assist.AssistStructure;
28 import android.autofillservice.cts.testcore.CtsAugmentedAutofillService.AugmentedFillRequest;
29 import android.content.ComponentName;
30 import android.service.autofill.augmented.FillRequest;
31 import android.util.Log;
32 import android.util.Pair;
33 import android.view.autofill.AutofillId;
34 import android.view.autofill.AutofillValue;
35 import android.view.inputmethod.InlineSuggestionsRequest;
36 
37 import androidx.annotation.NonNull;
38 import androidx.annotation.Nullable;
39 
40 import java.util.List;
41 import java.util.Objects;
42 import java.util.concurrent.CountDownLatch;
43 import java.util.concurrent.TimeUnit;
44 
45 /**
46  * Helper for common funcionalities.
47  */
48 public final class AugmentedHelper {
49 
50     private static final String TAG = AugmentedHelper.class.getSimpleName();
51 
52     @NonNull
getActivityName(@ullable FillRequest request)53     public static String getActivityName(@Nullable FillRequest request) {
54         if (request == null) return "N/A (null request)";
55 
56         final ComponentName componentName = request.getActivityComponent();
57         if (componentName == null) return "N/A (no component name)";
58 
59         return componentName.flattenToShortString();
60     }
61 
62     /**
63      * Sets the augmented capture service.
64      */
setAugmentedService(@onNull String service)65     public static void setAugmentedService(@NonNull String service) {
66         Log.d(TAG, "Setting service to " + service);
67         runShellCommand("cmd autofill set temporary-augmented-service 0 %s %d", service,
68                 MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS);
69     }
70 
71     /**
72      * Resets the content capture service.
73      */
resetAugmentedService()74     public static void resetAugmentedService() {
75         Log.d(TAG, "Resetting back to default service");
76         runShellCommand("cmd autofill set temporary-augmented-service 0");
77     }
78 
assertBasicRequestInfo(@onNull AugmentedFillRequest request, @NonNull Activity activity, @NonNull AutofillId expectedFocusedId, @Nullable AutofillValue expectedFocusedValue)79     public static void assertBasicRequestInfo(@NonNull AugmentedFillRequest request,
80             @NonNull Activity activity, @NonNull AutofillId expectedFocusedId,
81             @Nullable AutofillValue expectedFocusedValue) {
82         assertBasicRequestInfo(request, activity, expectedFocusedId, expectedFocusedValue, true);
83     }
84 
assertBasicRequestInfo(@onNull AugmentedFillRequest request, @NonNull Activity activity, @NonNull AutofillId expectedFocusedId, @Nullable AutofillValue expectedFocusedValue, boolean hasInlineRequest)85     public static void assertBasicRequestInfo(@NonNull AugmentedFillRequest request,
86             @NonNull Activity activity, @NonNull AutofillId expectedFocusedId,
87             @Nullable AutofillValue expectedFocusedValue, boolean hasInlineRequest) {
88         Objects.requireNonNull(activity);
89         Objects.requireNonNull(expectedFocusedId);
90         assertWithMessage("no AugmentedFillRequest").that(request).isNotNull();
91         assertWithMessage("no FillRequest on %s", request).that(request.request).isNotNull();
92         assertWithMessage("no FillController on %s", request).that(request.controller).isNotNull();
93         assertWithMessage("no FillCallback on %s", request).that(request.callback).isNotNull();
94         assertWithMessage("no CancellationSignal on %s", request).that(request.cancellationSignal)
95                 .isNotNull();
96         // NOTE: task id can change, we might need to set it in the activity's onCreate()
97         assertWithMessage("wrong task id on %s", request).that(request.request.getTaskId())
98                 .isEqualTo(activity.getTaskId());
99 
100         final ComponentName actualComponentName = request.request.getActivityComponent();
101         assertWithMessage("no activity name on %s", request).that(actualComponentName).isNotNull();
102         assertWithMessage("wrong activity name on %s", request).that(actualComponentName)
103                 .isEqualTo(activity.getComponentName());
104         final AutofillId actualFocusedId = request.request.getFocusedId();
105         assertWithMessage("no focused id on %s", request).that(actualFocusedId).isNotNull();
106         assertWithMessage("wrong focused id on %s", request).that(actualFocusedId)
107                 .isEqualTo(expectedFocusedId);
108         final AutofillValue actualFocusedValue = request.request.getFocusedValue();
109         if (expectedFocusedValue != null) {
110             assertWithMessage("no focused value on %s", request).that(
111                     actualFocusedValue).isNotNull();
112             assertAutofillValue(expectedFocusedValue, actualFocusedValue);
113         } else {
114             assertWithMessage("expecting null focused value on %s", request).that(
115                     actualFocusedValue).isNull();
116         }
117         if (expectedFocusedId.isNonVirtual()) {
118             final AssistStructure.ViewNode focusedViewNode = request.request.getFocusedViewNode();
119             assertWithMessage("no focused view node on %s", request).that(
120                     focusedViewNode).isNotNull();
121             assertWithMessage("wrong autofill id in focused view node %s", focusedViewNode).that(
122                     focusedViewNode.getAutofillId()).isEqualTo(expectedFocusedId);
123             assertWithMessage("unexpected autofill value in focused view node %s",
124                     focusedViewNode).that(focusedViewNode.getAutofillValue()).isEqualTo(
125                     expectedFocusedValue);
126             assertWithMessage("children nodes should not be populated for focused view node %s",
127                     focusedViewNode).that(
128                     focusedViewNode.getChildCount()).isEqualTo(0);
129         }
130         final InlineSuggestionsRequest inlineRequest =
131                 request.request.getInlineSuggestionsRequest();
132         if (hasInlineRequest) {
133             assertWithMessage("no inline request on %s", request).that(inlineRequest).isNotNull();
134         } else {
135             assertWithMessage("exist inline request on %s", request).that(inlineRequest).isNull();
136         }
137     }
138 
assertAutofillValue(@onNull AutofillValue expectedValue, @NonNull AutofillValue actualValue)139     public static void assertAutofillValue(@NonNull AutofillValue expectedValue,
140             @NonNull AutofillValue actualValue) {
141         // It only supports text values for now...
142         assertWithMessage("expected value is not text: %s", expectedValue)
143                 .that(expectedValue.isText()).isTrue();
144         assertAutofillValue(expectedValue.getTextValue().toString(), actualValue);
145     }
146 
assertAutofillValue(@onNull String expectedValue, @NonNull AutofillValue actualValue)147     public static void assertAutofillValue(@NonNull String expectedValue,
148             @NonNull AutofillValue actualValue) {
149         assertWithMessage("actual value is not text: %s", actualValue)
150                 .that(actualValue.isText()).isTrue();
151 
152         assertWithMessage("wrong autofill value").that(actualValue.getTextValue().toString())
153                 .isEqualTo(expectedValue);
154     }
155 
156     @NonNull
toString(@ullable List<Pair<AutofillId, AutofillValue>> values)157     public static String toString(@Nullable List<Pair<AutofillId, AutofillValue>> values) {
158         if (values == null) return "null";
159         final StringBuilder string = new StringBuilder("[");
160         final int size = values.size();
161         for (int i = 0; i < size; i++) {
162             final Pair<AutofillId, AutofillValue> value = values.get(i);
163             string.append(i).append(':').append(value.first).append('=')
164                    .append(Helper.toString(value.second));
165             if (i < size - 1) {
166                 string.append(", ");
167             }
168 
169         }
170         return string.append(']').toString();
171     }
172 
173     @NonNull
toString(@ullable FillRequest request)174     public static String toString(@Nullable FillRequest request) {
175         if (request == null) return "(null request)";
176 
177         final StringBuilder string =
178                 new StringBuilder("FillRequest[act=").append(getActivityName(request))
179                 .append(", taskId=").append(request.getTaskId());
180 
181         final AutofillId focusedId = request.getFocusedId();
182         if (focusedId != null) {
183             string.append(", focusedId=").append(focusedId);
184         }
185         final AutofillValue focusedValue = request.getFocusedValue();
186         if (focusedValue != null) {
187             string.append(", focusedValue=").append(focusedValue);
188         }
189 
190         return string.append(']').toString();
191     }
192 
193     // Used internally by UiBot to assert the UI
getContentDescriptionForUi(@onNull AutofillId focusedId)194     public static String getContentDescriptionForUi(@NonNull AutofillId focusedId) {
195         return "ui_for_" + focusedId;
196     }
197 
AugmentedHelper()198     private AugmentedHelper() {
199         throw new UnsupportedOperationException("contain static methods only");
200     }
201 
202     /**
203      * Awaits for a latch to be counted down.
204      */
await(@onNull CountDownLatch latch, @NonNull String fmt, @Nullable Object... args)205     public static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
206             @Nullable Object... args)
207             throws InterruptedException {
208         final boolean called = latch.await(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
209         if (!called) {
210             throw new IllegalStateException(String.format(fmt, args)
211                     + " in " + CONNECTION_TIMEOUT.ms() + "ms");
212         }
213     }
214 }
215