diff options
author | 2024-07-18 17:13:29 +0100 | |
---|---|---|
committer | 2024-07-23 02:59:18 +0100 | |
commit | cdceb40d7d11f2d2238f506748344d8bf348d3c0 (patch) | |
tree | d4a5deec327c08cf1bb5a6594feb0f19600d0a2f | |
parent | 6a3e6b1ae2bbf2eab928c03f46b05224b3d127dd (diff) |
Added AccessibilityCheckerManager which runs a11y checks and caches results
Bug: 326385939
Test: unit tests
Flag: com.android.server.accessibility.enable_a11y_checker_logging
Change-Id: Icfb13d105aaa2644ac6d527706341d0f627832b3
7 files changed, 462 insertions, 59 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerConstants.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerConstants.java new file mode 100644 index 000000000000..5a98a4027679 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerConstants.java @@ -0,0 +1,33 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.a11ychecker; + +import java.time.Duration; + +/** + * Constants used by the accessibility checker. + * + * @hide + */ +final class AccessibilityCheckerConstants { + + // The min required duration between two consecutive runs of the a11y checker. + static final Duration MIN_DURATION_BETWEEN_CHECKS = Duration.ofMinutes(1); + + // The max number of cached results at a time. + static final int MAX_CACHE_CAPACITY = 10000; +} diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java new file mode 100644 index 000000000000..8a2bc1dbf4f7 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java @@ -0,0 +1,169 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.a11ychecker; + +import static com.android.server.accessibility.a11ychecker.AccessibilityCheckerConstants.MAX_CACHE_CAPACITY; +import static com.android.server.accessibility.a11ychecker.AccessibilityCheckerConstants.MIN_DURATION_BETWEEN_CHECKS; + +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.UserIdInt; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.util.Slog; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.accessibility.Flags; +import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckResultReported; + +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset; +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck; +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult; +import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchy; +import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** + * The class responsible for running AccessibilityChecks on cached nodes and caching the results for + * logging. Results are cached and capped to limit the logging frequency and size. + * + * @hide + */ +public final class AccessibilityCheckerManager { + private static final String LOG_TAG = "AccessibilityCheckerManager"; + + private final PackageManager mPackageManager; + private final Set<AccessibilityHierarchyCheck> mHierarchyChecks; + private final ATFHierarchyBuilder mATFHierarchyBuilder; + private final Set<AccessibilityCheckResultReported> mCachedResults = new HashSet<>(); + @VisibleForTesting + final A11yCheckerTimer mTimer = new A11yCheckerTimer(); + + public AccessibilityCheckerManager(Context context) { + this(AccessibilityCheckPreset.getAccessibilityHierarchyChecksForPreset( + AccessibilityCheckPreset.LATEST), + (nodeInfo) -> AccessibilityHierarchyAndroid.newBuilder(nodeInfo, context).build(), + context.getPackageManager()); + } + + @VisibleForTesting + AccessibilityCheckerManager( + Set<AccessibilityHierarchyCheck> hierarchyChecks, + ATFHierarchyBuilder atfHierarchyBuilder, + PackageManager packageManager) { + this.mHierarchyChecks = hierarchyChecks; + this.mATFHierarchyBuilder = atfHierarchyBuilder; + this.mPackageManager = packageManager; + } + + /** + * If eligible, runs AccessibilityChecks on the given nodes and caches the results for later + * logging. Returns the check results for the given nodes. + */ + @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public Set<AccessibilityCheckResultReported> maybeRunA11yChecker( + List<AccessibilityNodeInfo> nodes, + @Nullable AccessibilityEvent accessibilityEvent, + ComponentName sourceComponentName, + @UserIdInt int userId) { + if (!shouldRunA11yChecker()) { + return Set.of(); + } + + Set<AccessibilityCheckResultReported> allResults = new HashSet<>(); + String defaultBrowserName = mPackageManager.getDefaultBrowserPackageNameAsUser(userId); + + try { + for (AccessibilityNodeInfo nodeInfo : nodes) { + // Skip browser results because they are mostly related to web content and not the + // browser app itself. + if (nodeInfo.getPackageName() == null + || nodeInfo.getPackageName().toString().equals(defaultBrowserName)) { + continue; + } + List<AccessibilityHierarchyCheckResult> checkResults = runChecksOnNode(nodeInfo); + Set<AccessibilityCheckResultReported> filteredResults = + AccessibilityCheckerUtils.processResults(nodeInfo, checkResults, + accessibilityEvent, mPackageManager, sourceComponentName); + allResults.addAll(filteredResults); + } + mCachedResults.addAll(allResults); + } catch (RuntimeException e) { + Slog.e(LOG_TAG, "An unknown error occurred while running a11y checker.", e); + } + + return allResults; + } + + private List<AccessibilityHierarchyCheckResult> runChecksOnNode( + AccessibilityNodeInfo nodeInfo) { + AccessibilityHierarchy checkableHierarchy = mATFHierarchyBuilder.getATFCheckableHierarchy( + nodeInfo); + List<AccessibilityHierarchyCheckResult> checkResults = new ArrayList<>(); + for (AccessibilityHierarchyCheck check : mHierarchyChecks) { + checkResults.addAll(check.runCheckOnHierarchy(checkableHierarchy)); + } + return checkResults; + } + + public Set<AccessibilityCheckResultReported> getCachedResults() { + return Collections.unmodifiableSet(mCachedResults); + } + + @VisibleForTesting + boolean shouldRunA11yChecker() { + if (!Flags.enableA11yCheckerLogging() || mCachedResults.size() == MAX_CACHE_CAPACITY) { + return false; + } + if (mTimer.getLastCheckTime() == null || mTimer.getLastCheckTime().plus( + MIN_DURATION_BETWEEN_CHECKS).isBefore(Instant.now())) { + mTimer.setLastCheckTime(Instant.now()); + return true; + } + return false; + } + + /** Timer class to facilitate testing with fake times. */ + @VisibleForTesting + static class A11yCheckerTimer { + private Instant mLastCheckTime = null; + + Instant getLastCheckTime() { + return mLastCheckTime; + } + + void setLastCheckTime(Instant newTime) { + mLastCheckTime = newTime; + } + } + + /** AccessibilityHierarchy wrapper to facilitate testing with fake hierarchies. */ + @VisibleForTesting + interface ATFHierarchyBuilder { + AccessibilityHierarchy getATFCheckableHierarchy(AccessibilityNodeInfo nodeInfo); + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java index 55af9a0cfd24..4171108770a9 100644 --- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java +++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java @@ -19,7 +19,6 @@ package com.android.server.accessibility.a11ychecker; import android.annotation.Nullable; import android.content.ComponentName; -import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Slog; @@ -62,6 +61,7 @@ import java.util.stream.Collectors; public class AccessibilityCheckerUtils { private static final String LOG_TAG = "AccessibilityCheckerUtils"; + @VisibleForTesting // LINT.IfChange static final Map<Class<? extends AccessibilityHierarchyCheck>, AccessibilityCheckClass> @@ -94,29 +94,22 @@ public class AccessibilityCheckerUtils { // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/a11ychecker/proto/a11ychecker.proto) static Set<AccessibilityCheckResultReported> processResults( - Context context, - AccessibilityNodeInfo nodeInfo, - List<AccessibilityHierarchyCheckResult> checkResults, - @Nullable AccessibilityEvent accessibilityEvent, - ComponentName a11yServiceComponentName) { - return processResults(nodeInfo, checkResults, accessibilityEvent, - context.getPackageManager(), a11yServiceComponentName); - } - - @VisibleForTesting - static Set<AccessibilityCheckResultReported> processResults( AccessibilityNodeInfo nodeInfo, List<AccessibilityHierarchyCheckResult> checkResults, @Nullable AccessibilityEvent accessibilityEvent, PackageManager packageManager, ComponentName a11yServiceComponentName) { String appPackageName = nodeInfo.getPackageName().toString(); + String nodePath = AccessibilityNodePathBuilder.createNodePath(nodeInfo); + if (nodePath == null) { + return Set.of(); + } AccessibilityCheckResultReported.Builder builder; try { builder = AccessibilityCheckResultReported.newBuilder() .setPackageName(appPackageName) .setAppVersionCode(getAppVersionCode(packageManager, appPackageName)) - .setUiElementPath(AccessibilityNodePathBuilder.createNodePath(nodeInfo)) + .setUiElementPath(nodePath) .setActivityName(getActivityName(packageManager, accessibilityEvent)) .setWindowTitle(getWindowTitle(nodeInfo)) .setSourceComponentName(a11yServiceComponentName.flattenToString()) diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java index bbfb217d925e..465ce0de79a4 100644 --- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java +++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityNodePathBuilder.java @@ -47,12 +47,15 @@ public final class AccessibilityNodePathBuilder { * * <p>This format is consistent with elements paths in Pre-Launch Reports and the Accessibility * Scanner, starting from the window's root node instead of the first resource name. - * TODO (b/344607035): link to ClusteringUtils when AATF is merged in main. + * See {@link com.google.android.apps.common.testing.accessibility.framework.ClusteringUtils}. */ public static @Nullable String createNodePath(@NonNull AccessibilityNodeInfo nodeInfo) { + String packageName = nodeInfo.getPackageName().toString(); + if (packageName == null) { + return null; + } StringBuilder resourceIdBuilder = getNodePathBuilder(nodeInfo); - return resourceIdBuilder == null ? null : String.valueOf(nodeInfo.getPackageName()) + ':' - + resourceIdBuilder; + return resourceIdBuilder == null ? null : packageName + ':' + resourceIdBuilder; } private static @Nullable StringBuilder getNodePathBuilder(AccessibilityNodeInfo nodeInfo) { @@ -84,20 +87,23 @@ public final class AccessibilityNodePathBuilder { //Returns the part of the element's View ID resource name after the qualifier // "package_name:id/" or the last '/', when available. Otherwise, returns the element's // simple class name. - private static CharSequence getShortUiElementName(AccessibilityNodeInfo nodeInfo) { + private static @Nullable CharSequence getShortUiElementName(AccessibilityNodeInfo nodeInfo) { String viewIdResourceName = nodeInfo.getViewIdResourceName(); - if (viewIdResourceName != null) { - String idQualifier = ":id/"; - int idQualifierStartIndex = viewIdResourceName.indexOf(idQualifier); - int unqualifiedNameStartIndex = idQualifierStartIndex == -1 ? 0 - : (idQualifierStartIndex + idQualifier.length()); - return viewIdResourceName.substring(unqualifiedNameStartIndex); + if (viewIdResourceName == null) { + return getSimpleClassName(nodeInfo); } - return getSimpleClassName(nodeInfo); + String idQualifier = ":id/"; + int idQualifierStartIndex = viewIdResourceName.indexOf(idQualifier); + int unqualifiedNameStartIndex = + idQualifierStartIndex == -1 ? 0 : (idQualifierStartIndex + idQualifier.length()); + return viewIdResourceName.substring(unqualifiedNameStartIndex); } - private static CharSequence getSimpleClassName(AccessibilityNodeInfo nodeInfo) { + private static @Nullable CharSequence getSimpleClassName(AccessibilityNodeInfo nodeInfo) { CharSequence name = nodeInfo.getClassName(); + if (name == null) { + return null; + } for (int i = name.length() - 1; i > 0; i--) { char ithChar = name.charAt(i); if (ithChar == '.' || ithChar == '$') { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java new file mode 100644 index 000000000000..f92eaabab3f7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java @@ -0,0 +1,192 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.a11ychecker; + +import static com.android.server.accessibility.Flags.FLAG_ENABLE_A11Y_CHECKER_LOGGING; +import static com.android.server.accessibility.a11ychecker.AccessibilityCheckerConstants.MIN_DURATION_BETWEEN_CHECKS; +import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_CLASS_NAME; +import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME; +import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_ACTIVITY_NAME; +import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_DEFAULT_BROWSER; +import static com.android.server.accessibility.a11ychecker.TestUtils.createAtom; +import static com.android.server.accessibility.a11ychecker.TestUtils.getMockPackageManagerWithInstalledApps; +import static com.android.server.accessibility.a11ychecker.TestUtils.getTestAccessibilityEvent; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.view.accessibility.AccessibilityNodeInfo; + +import androidx.test.runner.AndroidJUnit4; + +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult; +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck; +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult; +import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck; +import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchy; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +public class AccessibilityCheckerManagerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private AccessibilityCheckerManager mAccessibilityCheckerManager; + + @Before + public void setup() throws PackageManager.NameNotFoundException { + PackageManager mockPackageManager = getMockPackageManagerWithInstalledApps(); + mAccessibilityCheckerManager = new AccessibilityCheckerManager(setupMockChecks(), + nodeInfo -> mock(AccessibilityHierarchy.class), mockPackageManager); + } + + + @Test + @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING) + public void shouldRunA11yChecker_firstUpdate() { + assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isTrue(); + } + + @Test + @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING) + public void shouldRunA11yChecker_minDurationPassed() { + mAccessibilityCheckerManager.mTimer.setLastCheckTime( + Instant.now().minus(MIN_DURATION_BETWEEN_CHECKS.plus(Duration.ofSeconds(2)))); + assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isTrue(); + } + + @Test + @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING) + public void shouldRunA11yChecker_tooEarly() { + mAccessibilityCheckerManager.mTimer.setLastCheckTime( + Instant.now().minus(MIN_DURATION_BETWEEN_CHECKS.minus(Duration.ofSeconds(2)))); + assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isFalse(); + } + + @Test + @DisableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING) + public void shouldRunA11yChecker_featureDisabled() { + assertThat(mAccessibilityCheckerManager.shouldRunA11yChecker()).isFalse(); + } + + @Test + @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING) + public void maybeRunA11yChecker_happyPath() { + AccessibilityNodeInfo mockNodeInfo1 = + new MockAccessibilityNodeInfoBuilder() + .setViewIdResourceName("node1") + .build(); + AccessibilityNodeInfo mockNodeInfo2 = + new MockAccessibilityNodeInfoBuilder() + .setViewIdResourceName("node2") + .build(); + + Set<A11yCheckerProto.AccessibilityCheckResultReported> results = + mAccessibilityCheckerManager.maybeRunA11yChecker( + List.of(mockNodeInfo1, mockNodeInfo2), getTestAccessibilityEvent(), + new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, + TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0); + + assertThat(results).containsExactly( + createAtom(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME, + A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK, + A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2), + createAtom(/*viewIdResourceName=*/ "node2", TEST_ACTIVITY_NAME, + A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK, + A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2) + ); + } + + @Test + @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING) + public void maybeRunA11yChecker_skipsNodesFromDefaultBrowser() { + AccessibilityNodeInfo mockNodeInfo = + new MockAccessibilityNodeInfoBuilder() + .setPackageName(TEST_DEFAULT_BROWSER) + .setViewIdResourceName("node1") + .build(); + + Set<A11yCheckerProto.AccessibilityCheckResultReported> results = + mAccessibilityCheckerManager.maybeRunA11yChecker( + List.of(mockNodeInfo), getTestAccessibilityEvent(), + new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, + TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0); + + assertThat(results).isEmpty(); + } + + @Test + @EnableFlags(FLAG_ENABLE_A11Y_CHECKER_LOGGING) + public void maybeRunA11yChecker_doesNotStoreDuplicates() { + AccessibilityNodeInfo mockNodeInfo = + new MockAccessibilityNodeInfoBuilder() + .setViewIdResourceName("node1") + .build(); + AccessibilityNodeInfo mockNodeInfoDuplicate = + new MockAccessibilityNodeInfoBuilder() + .setViewIdResourceName("node1") + .build(); + + Set<A11yCheckerProto.AccessibilityCheckResultReported> results = + mAccessibilityCheckerManager.maybeRunA11yChecker( + List.of(mockNodeInfo, mockNodeInfoDuplicate), getTestAccessibilityEvent(), + new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, + TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0); + + assertThat(results).containsExactly( + createAtom(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME, + A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK, + A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2) + ); + } + + private Set<AccessibilityHierarchyCheck> setupMockChecks() { + AccessibilityHierarchyCheck mockCheck1 = mock(AccessibilityHierarchyCheck.class); + AccessibilityHierarchyCheckResult infoTypeResult = + new AccessibilityHierarchyCheckResult( + TouchTargetSizeCheck.class, + AccessibilityCheckResult.AccessibilityCheckResultType.INFO, null, 1, null); + when(mockCheck1.runCheckOnHierarchy(any())).thenReturn(List.of(infoTypeResult)); + + AccessibilityHierarchyCheck mockCheck2 = mock(AccessibilityHierarchyCheck.class); + AccessibilityHierarchyCheckResult errorTypeResult = + new AccessibilityHierarchyCheckResult( + TouchTargetSizeCheck.class, + AccessibilityCheckResult.AccessibilityCheckResultType.ERROR, null, 2, + null); + when(mockCheck2.runCheckOnHierarchy(any())).thenReturn(List.of(errorTypeResult)); + + return Set.of(mockCheck1, mockCheck2); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java index 90d427596ab2..141f174e9615 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java @@ -18,12 +18,9 @@ package com.android.server.accessibility.a11ychecker; import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_CLASS_NAME; import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME; -import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_VERSION_CODE; -import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_ACTIVITY_NAME; -import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_APP_PACKAGE_NAME; -import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_APP_VERSION_CODE; -import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_WINDOW_TITLE; +import static com.android.server.accessibility.a11ychecker.TestUtils.createAtom; import static com.android.server.accessibility.a11ychecker.TestUtils.getMockPackageManagerWithInstalledApps; +import static com.android.server.accessibility.a11ychecker.TestUtils.getTestAccessibilityEvent; import static com.google.common.truth.Truth.assertThat; @@ -99,9 +96,11 @@ public class AccessibilityCheckerUtilsTest { TEST_A11Y_SERVICE_CLASS_NAME)); assertThat(atoms).containsExactly( - createAtom(A11yCheckerProto.AccessibilityCheckClass.SPEAKABLE_TEXT_PRESENT_CHECK, + createAtom("TargetNode", "", + A11yCheckerProto.AccessibilityCheckClass.SPEAKABLE_TEXT_PRESENT_CHECK, A11yCheckerProto.AccessibilityCheckResultType.WARNING, 1), - createAtom(A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK, + createAtom("TargetNode", "", + A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK, A11yCheckerProto.AccessibilityCheckResultType.ERROR, 2) ); } @@ -140,10 +139,7 @@ public class AccessibilityCheckerUtilsTest { @Test public void getActivityName_hasWindowStateChangedEvent_returnsActivityName() { - AccessibilityEvent accessibilityEvent = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - accessibilityEvent.setPackageName(TEST_APP_PACKAGE_NAME); - accessibilityEvent.setClassName(TEST_ACTIVITY_NAME); + AccessibilityEvent accessibilityEvent = getTestAccessibilityEvent(); assertThat(AccessibilityCheckerUtils.getActivityName(mMockPackageManager, accessibilityEvent)).isEqualTo("MainActivity"); @@ -164,24 +160,4 @@ public class AccessibilityCheckerUtilsTest { .containsExactlyElementsIn(latestCheckClasses); } - - private static A11yCheckerProto.AccessibilityCheckResultReported createAtom( - A11yCheckerProto.AccessibilityCheckClass checkClass, - A11yCheckerProto.AccessibilityCheckResultType resultType, - int resultId) { - return A11yCheckerProto.AccessibilityCheckResultReported.newBuilder() - .setPackageName(TEST_APP_PACKAGE_NAME) - .setAppVersionCode(TEST_APP_VERSION_CODE) - .setUiElementPath(TEST_APP_PACKAGE_NAME + ":TargetNode") - .setWindowTitle(TEST_WINDOW_TITLE) - .setActivityName("") - .setSourceComponentName(new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, - TEST_A11Y_SERVICE_CLASS_NAME).flattenToString()) - .setSourceVersionCode(TEST_A11Y_SERVICE_SOURCE_VERSION_CODE) - .setResultCheckClass(checkClass) - .setResultType(resultType) - .setResultId(resultId) - .build(); - } - } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java index a04bbee05730..ec1a25554a81 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java @@ -16,6 +16,7 @@ package com.android.server.accessibility.a11ychecker; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.when; import android.annotation.Nullable; @@ -23,24 +24,27 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.view.accessibility.AccessibilityEvent; import org.mockito.Mockito; public class TestUtils { static final String TEST_APP_PACKAGE_NAME = "com.example.app"; static final int TEST_APP_VERSION_CODE = 12321; - static final String TEST_ACTIVITY_NAME = "com.example.app.MainActivity"; + static final String TEST_ACTIVITY_NAME = "MainActivity"; + static final String QUALIFIED_TEST_ACTIVITY_NAME = "com.example.app.MainActivity"; static final String TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME = "com.assistive.app"; static final String TEST_A11Y_SERVICE_CLASS_NAME = "MyA11yService"; static final int TEST_A11Y_SERVICE_SOURCE_VERSION_CODE = 333555; static final String TEST_WINDOW_TITLE = "Example window"; + static final String TEST_DEFAULT_BROWSER = "com.android.chrome"; static PackageManager getMockPackageManagerWithInstalledApps() throws PackageManager.NameNotFoundException { PackageManager mockPackageManager = Mockito.mock(PackageManager.class); ActivityInfo testActivityInfo = getTestActivityInfo(); ComponentName testActivityComponentName = new ComponentName(TEST_APP_PACKAGE_NAME, - TEST_ACTIVITY_NAME); + QUALIFIED_TEST_ACTIVITY_NAME); when(mockPackageManager.getActivityInfo(testActivityComponentName, 0)) .thenReturn(testActivityInfo); @@ -50,13 +54,15 @@ public class TestUtils { when(mockPackageManager.getPackageInfo(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, 0)) .thenReturn(createPackageInfo(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, TEST_A11Y_SERVICE_SOURCE_VERSION_CODE, null)); + when(mockPackageManager.getDefaultBrowserPackageNameAsUser(anyInt())).thenReturn( + TEST_DEFAULT_BROWSER); return mockPackageManager; } static ActivityInfo getTestActivityInfo() { ActivityInfo activityInfo = new ActivityInfo(); activityInfo.packageName = TEST_APP_PACKAGE_NAME; - activityInfo.name = TEST_ACTIVITY_NAME; + activityInfo.name = QUALIFIED_TEST_ACTIVITY_NAME; return activityInfo; } @@ -69,6 +75,34 @@ public class TestUtils { packageInfo.activities = new ActivityInfo[]{activityInfo}; } return packageInfo; + } + + static AccessibilityEvent getTestAccessibilityEvent() { + AccessibilityEvent accessibilityEvent = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + accessibilityEvent.setPackageName(TEST_APP_PACKAGE_NAME); + accessibilityEvent.setClassName(QUALIFIED_TEST_ACTIVITY_NAME); + return accessibilityEvent; + } + static A11yCheckerProto.AccessibilityCheckResultReported createAtom( + String viewIdResourceName, + String activityName, + A11yCheckerProto.AccessibilityCheckClass checkClass, + A11yCheckerProto.AccessibilityCheckResultType resultType, + int resultId) { + return A11yCheckerProto.AccessibilityCheckResultReported.newBuilder() + .setPackageName(TEST_APP_PACKAGE_NAME) + .setAppVersionCode(TEST_APP_VERSION_CODE) + .setUiElementPath(TEST_APP_PACKAGE_NAME + ":" + viewIdResourceName) + .setWindowTitle(TEST_WINDOW_TITLE) + .setActivityName(activityName) + .setSourceComponentName(new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME, + TEST_A11Y_SERVICE_CLASS_NAME).flattenToString()) + .setSourceVersionCode(TEST_A11Y_SERVICE_SOURCE_VERSION_CODE) + .setResultCheckClass(checkClass) + .setResultType(resultType) + .setResultId(resultId) + .build(); } } |