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
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 0000000..5a98a40
--- /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 0000000..8a2bc1d
--- /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 55af9a0..4171108 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 @@
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 @@
public class AccessibilityCheckerUtils {
private static final String LOG_TAG = "AccessibilityCheckerUtils";
+
@VisibleForTesting
// LINT.IfChange
static final Map<Class<? extends AccessibilityHierarchyCheck>, AccessibilityCheckClass>
@@ -94,29 +94,22 @@
// 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 bbfb217..465ce0d 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 @@
*
* <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 @@
//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 0000000..f92eaab
--- /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 90d4275..141f174 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 @@
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 @@
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 @@
@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 @@
.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 a04bbee..ec1a255 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.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 @@
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 @@
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();
}
}