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