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();
     }
 }