Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4424cf1 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Store source activity name per window in the AccessiblityCache" into main

parents 22dfa608 16553313
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.view.accessibility;

import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;

import android.annotation.Nullable;
import android.os.Build;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -48,6 +49,8 @@ public class AccessibilityCache {

    private boolean mEnabled = true;

    private final SparseArray<String> mWindowIdToEventSourceClassName = new SparseArray<>();

    /**
     * {@link AccessibilityEvent} types that are critical for the cache to stay up to date
     *
@@ -273,8 +276,11 @@ public class AccessibilityCache {
                    clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId());
                } break;

                case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
                case AccessibilityEvent.TYPE_WINDOWS_CHANGED: {
                    mValidWindowCacheTimeStamp = event.getEventTime();
                    if (event.getWindowChanges() == AccessibilityEvent.WINDOWS_CHANGE_REMOVED) {
                        mWindowIdToEventSourceClassName.remove(event.getWindowId());
                    }
                    if (event.getWindowChanges()
                            == AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED) {
                        // Don't need to clear all cache. Unless the changes are related to
@@ -282,8 +288,15 @@ public class AccessibilityCache {
                        clearWindowCacheLocked();
                        break;
                    }
                    clear();
                }
                break;
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
                    mValidWindowCacheTimeStamp = event.getEventTime();
                    if (event.getContentChangeTypes() == 0 && event.getClassName() != null) {
                        mWindowIdToEventSourceClassName.put(event.getWindowId(),
                                event.getClassName().toString());
                    }
                    clear();
                } break;
            }
@@ -907,6 +920,12 @@ public class AccessibilityCache {
        }
    }

    /** Returns the source class associated with the window with the given id. */
    @Nullable
    public String getEventSourceClassName(int windowId) {
        return mWindowIdToEventSourceClassName.get(windowId);
    }

    // Layer of indirection included to break dependency chain for testing
    public static class AccessibilityNodeRefresher {
        /** Refresh the given AccessibilityNodeInfo object. */
+22 −0
Original line number Diff line number Diff line
@@ -1053,6 +1053,28 @@ public class AccessibilityCacheTest {
        assertFalse(mAccessibilityCache.isNodeInCache(childInfo));
    }

    @Test
    public void getEventSourceClassName_windowStateChangedThenRemoved() {
        final String sourceActivityClassName = "com.example.SomeActivity";
        final AccessibilityEvent windowStateChangedEvent = new AccessibilityEvent(
                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
        final View mockView = getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1);
        windowStateChangedEvent.setSource(mockView);
        windowStateChangedEvent.setClassName(sourceActivityClassName);

        mAccessibilityCache.onAccessibilityEvent(windowStateChangedEvent);
        assertEquals(mAccessibilityCache.getEventSourceClassName(WINDOW_ID_1),
                sourceActivityClassName);

        final AccessibilityEvent windowRemovedEvent = new AccessibilityEvent(
                AccessibilityEvent.TYPE_WINDOWS_CHANGED);
        windowRemovedEvent.setWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED);
        windowRemovedEvent.setSource(mockView);

        mAccessibilityCache.onAccessibilityEvent(windowRemovedEvent);
        assertNull(mAccessibilityCache.getEventSourceClassName(WINDOW_ID_1));
    }

    private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
        AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
        windowInfo.setId(windowId);
+4 −7
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ 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;
@@ -60,6 +59,7 @@ public final class AccessibilityCheckerManager {
    private final Set<AccessibilityHierarchyCheck> mHierarchyChecks;
    private final ATFHierarchyBuilder mATFHierarchyBuilder;
    private final Set<AccessibilityCheckResultReported> mCachedResults = new HashSet<>();

    @VisibleForTesting
    final A11yCheckerTimer mTimer = new A11yCheckerTimer();

@@ -86,10 +86,8 @@ public final class AccessibilityCheckerManager {
     */
    @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
    public Set<AccessibilityCheckResultReported> maybeRunA11yChecker(
            List<AccessibilityNodeInfo> nodes,
            @Nullable AccessibilityEvent accessibilityEvent,
            ComponentName sourceComponentName,
            @UserIdInt int userId) {
            List<AccessibilityNodeInfo> nodes, @Nullable String sourceEventClassName,
            ComponentName a11yServiceComponentName, @UserIdInt int userId) {
        if (!shouldRunA11yChecker()) {
            return Set.of();
        }
@@ -108,14 +106,13 @@ public final class AccessibilityCheckerManager {
                List<AccessibilityHierarchyCheckResult> checkResults = runChecksOnNode(nodeInfo);
                Set<AccessibilityCheckResultReported> filteredResults =
                        AccessibilityCheckerUtils.processResults(nodeInfo, checkResults,
                                accessibilityEvent, mPackageManager, sourceComponentName);
                                sourceEventClassName, mPackageManager, a11yServiceComponentName);
                allResults.addAll(filteredResults);
            }
            mCachedResults.addAll(allResults);
        } catch (RuntimeException e) {
            Slog.e(LOG_TAG, "An unknown error occurred while running a11y checker.", e);
        }

        return allResults;
    }

+14 −22
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.content.ComponentName;
import android.content.pm.PackageInfo;
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;
@@ -96,7 +95,7 @@ public class AccessibilityCheckerUtils {
    static Set<AccessibilityCheckResultReported> processResults(
            AccessibilityNodeInfo nodeInfo,
            List<AccessibilityHierarchyCheckResult> checkResults,
            @Nullable AccessibilityEvent accessibilityEvent,
            @Nullable String activityClassName,
            PackageManager packageManager,
            ComponentName a11yServiceComponentName) {
        String appPackageName = nodeInfo.getPackageName().toString();
@@ -110,7 +109,8 @@ public class AccessibilityCheckerUtils {
                    .setPackageName(appPackageName)
                    .setAppVersionCode(getAppVersionCode(packageManager, appPackageName))
                    .setUiElementPath(nodePath)
                    .setActivityName(getActivityName(packageManager, accessibilityEvent))
                    .setActivityName(
                            getActivityName(packageManager, appPackageName, activityClassName))
                    .setWindowTitle(getWindowTitle(nodeInfo))
                    .setSourceComponentName(a11yServiceComponentName.flattenToString())
                    .setSourceVersionCode(
@@ -140,32 +140,24 @@ public class AccessibilityCheckerUtils {
    }

    /**
     * Returns the simple class name of the Activity providing the cache update, if available,
     * Returns the simple class name of the Activity associated with the window, if available,
     * or an empty String if not.
     */
    @VisibleForTesting
    static String getActivityName(
            PackageManager packageManager, @Nullable AccessibilityEvent accessibilityEvent) {
        if (accessibilityEvent == null) {
            PackageManager packageManager, String packageName, @Nullable String activityClassName) {
        if (activityClassName == null) {
            return "";
        }
        CharSequence activityName = accessibilityEvent.getClassName();
        if (accessibilityEvent.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
                && accessibilityEvent.getPackageName() != null
                && activityName != null) {
        try {
            // Check class is for a valid Activity.
                packageManager
                        .getActivityInfo(
                                new ComponentName(accessibilityEvent.getPackageName().toString(),
                                        activityName.toString()), 0);
                int qualifierEnd = activityName.toString().lastIndexOf('.');
                return activityName.toString().substring(qualifierEnd + 1);
            packageManager.getActivityInfo(new ComponentName(packageName, activityClassName), 0);
            int qualifierEnd = activityClassName.lastIndexOf('.');
            return activityClassName.substring(qualifierEnd + 1);
        } catch (PackageManager.NameNotFoundException e) {
            // No need to spam the logs. This is very frequent when the class doesn't match
            // an activity.
        }
        }
        return "";
    }

+4 −4
Original line number Diff line number Diff line
@@ -18,13 +18,13 @@ 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.QUALIFIED_TEST_ACTIVITY_NAME;
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;

@@ -114,7 +114,7 @@ public class AccessibilityCheckerManagerTest {

        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
                mAccessibilityCheckerManager.maybeRunA11yChecker(
                        List.of(mockNodeInfo1, mockNodeInfo2), getTestAccessibilityEvent(),
                        List.of(mockNodeInfo1, mockNodeInfo2), QUALIFIED_TEST_ACTIVITY_NAME,
                        new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                                TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);

@@ -139,7 +139,7 @@ public class AccessibilityCheckerManagerTest {

        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
                mAccessibilityCheckerManager.maybeRunA11yChecker(
                        List.of(mockNodeInfo), getTestAccessibilityEvent(),
                        List.of(mockNodeInfo), QUALIFIED_TEST_ACTIVITY_NAME,
                        new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                                TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);

@@ -160,7 +160,7 @@ public class AccessibilityCheckerManagerTest {

        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
                mAccessibilityCheckerManager.maybeRunA11yChecker(
                        List.of(mockNodeInfo, mockNodeInfoDuplicate), getTestAccessibilityEvent(),
                        List.of(mockNodeInfo, mockNodeInfoDuplicate), QUALIFIED_TEST_ACTIVITY_NAME,
                        new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                                TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);

Loading