[OnDemand AT] Factor histogram logging logic into separate class

To improve code readability and maintainability, factor out UMA
histogram logging from WebContentsAccessibilityImpl into a separate
component, the AccessibilityHistogramLogger.
This will help when adding logging to track impact of new features like
OnDemand AT.

Change-Id: I3288bd04365fc892b6143595d3b51a62e1439ee7
Bug: 1227192
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3824049
Reviewed-by: Mark Schillaci <mschillaci@google.com>
Reviewed-by: Bo Liu <boliu@chromium.org>
Commit-Queue: Amanda Lin Dietz <aldietz@google.com>
Cr-Commit-Position: refs/heads/main@{#1035240}
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 4bcaf77..3c6aafb 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -240,6 +240,7 @@
     "java/src/org/chromium/content/browser/accessibility/AccessibilityActionAndEventTracker.java",
     "java/src/org/chromium/content/browser/accessibility/AccessibilityDelegate.java",
     "java/src/org/chromium/content/browser/accessibility/AccessibilityEventDispatcher.java",
+    "java/src/org/chromium/content/browser/accessibility/AccessibilityHistogramRecorder.java",
     "java/src/org/chromium/content/browser/accessibility/AccessibilityNodeInfoUtils.java",
     "java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityState.java",
     "java/src/org/chromium/content/browser/accessibility/OViewStructureBuilder.java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityHistogramRecorder.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityHistogramRecorder.java
new file mode 100644
index 0000000..5886238
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/AccessibilityHistogramRecorder.java
@@ -0,0 +1,193 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser.accessibility;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.content_public.browser.ContentFeatureList;
+
+/**
+ * Helper class for recording UMA histograms of accessibility events
+ */
+public class AccessibilityHistogramRecorder {
+    // OnDemand AX Mode histogram values
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String PERCENTAGE_DROPPED_HISTOGRAM =
+            "Accessibility.Android.OnDemand.PercentageDropped";
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_COMPLETE =
+            "Accessibility.Android.OnDemand.PercentageDropped.Complete";
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_BASIC =
+            "Accessibility.Android.OnDemand.PercentageDropped.Basic";
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String EVENTS_DROPPED_HISTOGRAM =
+            "Accessibility.Android.OnDemand.EventsDropped";
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String ONE_HUNDRED_PERCENT_HISTOGRAM =
+            "Accessibility.Android.OnDemand.OneHundredPercentEventsDropped";
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_COMPLETE =
+            "Accessibility.Android.OnDemand.OneHundredPercentEventsDropped.Complete";
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_BASIC =
+            "Accessibility.Android.OnDemand.OneHundredPercentEventsDropped.Basic";
+
+    private static final int EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET = 1;
+    private static final int EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET = 10000;
+    private static final int EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT = 100;
+
+    // Node cache histogram values
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String CACHE_MAX_NODES_HISTOGRAM =
+            "Accessibility.Android.Cache.MaxNodesInCache";
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static final String CACHE_PERCENTAGE_RETRIEVED_FROM_CACHE_HISTOGRAM =
+            "Accessibility.Android.Cache.PercentageRetrievedFromCache";
+
+    private static final int CACHE_MAX_NODES_MIN_BUCKET = 1;
+    private static final int CACHE_MAX_NODES_MAX_BUCKET = 3000;
+    private static final int CACHE_MAX_NODES_BUCKET_COUNT = 100;
+
+    // These track the total number of enqueued events, and the total number of dispatched events,
+    // so we can report the percentage/number of dropped events.
+    private int mTotalEnqueuedEvents;
+    private int mTotalDispatchedEvents;
+
+    // These track the usage of the |mNodeInfoCache| to report metrics on the max number of items
+    // that were stored in the cache, and the percentage of requests retrieved from the cache.
+    private int mMaxNodesInCache;
+    private int mNodeWasReturnedFromCache;
+    private int mNodeWasCreatedFromScratch;
+
+    /**
+     * Increment the count of enqueued events
+     */
+    public void incrementEnqueuedEvents() {
+        mTotalEnqueuedEvents++;
+    }
+
+    /**
+     * Increment the count of dispatched events
+     */
+    public void incrementDispatchedEvents() {
+        mTotalDispatchedEvents++;
+    }
+
+    /**
+     * Update the value of max nodes in the cache given the current size of the node info cache
+     * @param nodeInfoCacheSize the size of the node info cache
+     */
+    public void updateMaxNodesInCache(int nodeInfoCacheSize) {
+        mMaxNodesInCache = Math.max(mMaxNodesInCache, nodeInfoCacheSize);
+    }
+
+    /**
+     * Increment the count of instances when a node was returned from the cache
+     */
+    public void incrementNodeWasReturnedFromCache() {
+        mNodeWasReturnedFromCache++;
+    }
+
+    /**
+     * Increment the count of instances when a node was created from scratch
+     */
+    public void incrementNodeWasCreatedFromScratch() {
+        mNodeWasCreatedFromScratch++;
+    }
+
+    /**
+     * Record UMA histograms for all tracked data
+     */
+    public void recordHistograms() {
+        // If the OnDemand feature is enabled, log UMA metrics and reset counters.
+        if (ContentFeatureList.isEnabled(ContentFeatureList.ON_DEMAND_ACCESSIBILITY_EVENTS)) {
+            recordEventsHistograms();
+        }
+
+        // Always track the histograms for cache usage statistics.
+        recordCacheHistograms();
+    }
+
+    /**
+     * Record UMA histograms for the event counts for the OnDemand feature
+     */
+    public void recordEventsHistograms() {
+        // To investigate whether adding more AXModes could be beneficial, track separate
+        // stats when both the ComputeAXMode and OnDemand features are enabled.
+        boolean isComputeAXModeEnabled =
+                ContentFeatureList.isEnabled(ContentFeatureList.COMPUTE_AX_MODE);
+        // There are only 2 AXModes, kAXModeComplete is used when a screenreader is active.
+        boolean isAXModeComplete = BrowserAccessibilityState.screenReaderMode();
+
+        // If we did not enqueue any events, we can ignore the data as a trivial case.
+        if (mTotalEnqueuedEvents > 0) {
+            // Log the percentage dropped (dispatching 0 events should be 100% dropped).
+            int percentSent = (int) (mTotalDispatchedEvents * 1.0 / mTotalEnqueuedEvents * 100.0);
+            RecordHistogram.recordPercentageHistogram(
+                    PERCENTAGE_DROPPED_HISTOGRAM, 100 - percentSent);
+            // Log the percentage dropped per AXMode as well.
+            if (isComputeAXModeEnabled) {
+                RecordHistogram.recordPercentageHistogram(isAXModeComplete
+                                ? PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_COMPLETE
+                                : PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_BASIC,
+                        100 - percentSent);
+            }
+
+            // Log the total number of dropped events. (Not relevant to be tracked per AXMode)
+            RecordHistogram.recordCustomCountHistogram(EVENTS_DROPPED_HISTOGRAM,
+                    mTotalEnqueuedEvents - mTotalDispatchedEvents,
+                    EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET, EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET,
+                    EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT);
+
+            // If 100% of events were dropped, also track the number of dropped events in a
+            // separate bucket.
+            if (percentSent == 0) {
+                RecordHistogram.recordCustomCountHistogram(ONE_HUNDRED_PERCENT_HISTOGRAM,
+                        mTotalEnqueuedEvents - mTotalDispatchedEvents,
+                        EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET, EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET,
+                        EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT);
+
+                // Log the 100% events count per AXMode as well.
+                if (isComputeAXModeEnabled) {
+                    RecordHistogram.recordCustomCountHistogram(isAXModeComplete
+                                    ? ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_COMPLETE
+                                    : ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_BASIC,
+                            mTotalEnqueuedEvents - mTotalDispatchedEvents,
+                            EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET,
+                            EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET,
+                            EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT);
+                }
+            }
+        }
+
+        // Reset counters.
+        mTotalEnqueuedEvents = 0;
+        mTotalDispatchedEvents = 0;
+    }
+
+    /**
+     *  Record UMA histograms for cache usage statistics
+     */
+    public void recordCacheHistograms() {
+        RecordHistogram.recordCustomCountHistogram(CACHE_MAX_NODES_HISTOGRAM, mMaxNodesInCache,
+                CACHE_MAX_NODES_MIN_BUCKET, CACHE_MAX_NODES_MAX_BUCKET,
+                CACHE_MAX_NODES_BUCKET_COUNT);
+
+        int totalNodeRequests = mNodeWasReturnedFromCache + mNodeWasCreatedFromScratch;
+        int percentFromCache = (int) (mNodeWasReturnedFromCache * 1.0 / totalNodeRequests * 100.0);
+
+        RecordHistogram.recordPercentageHistogram(
+                CACHE_PERCENTAGE_RETRIEVED_FROM_CACHE_HISTOGRAM, percentFromCache);
+
+        // Reset counters.
+        mMaxNodesInCache = 0;
+        mNodeWasReturnedFromCache = 0;
+        mNodeWasCreatedFromScratch = 0;
+    }
+}
\ No newline at end of file
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index a9c772a4..a55db4a 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -86,7 +86,6 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.content.browser.WindowEventObserver;
 import org.chromium.content.browser.WindowEventObserverManager;
 import org.chromium.content.browser.accessibility.AccessibilityDelegate.AccessibilityCoordinates;
@@ -169,42 +168,6 @@
     // once per this time interval in milliseconds for a given focused node.
     private static final int CONTENT_INVALID_THROTTLE_DELAY = 4500;
 
-    // These are constant names of UMA histograms, and values for custom count histogram.
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String PERCENTAGE_DROPPED_HISTOGRAM =
-            "Accessibility.Android.OnDemand.PercentageDropped";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_COMPLETE =
-            "Accessibility.Android.OnDemand.PercentageDropped.Complete";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_BASIC =
-            "Accessibility.Android.OnDemand.PercentageDropped.Basic";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String EVENTS_DROPPED_HISTOGRAM =
-            "Accessibility.Android.OnDemand.EventsDropped";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String ONE_HUNDRED_PERCENT_HISTOGRAM =
-            "Accessibility.Android.OnDemand.OneHundredPercentEventsDropped";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_COMPLETE =
-            "Accessibility.Android.OnDemand.OneHundredPercentEventsDropped.Complete";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_BASIC =
-            "Accessibility.Android.OnDemand.OneHundredPercentEventsDropped.Basic";
-    private static final int EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET = 1;
-    private static final int EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET = 10000;
-    private static final int EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT = 100;
-
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String CACHE_MAX_NODES_HISTOGRAM =
-            "Accessibility.Android.Cache.MaxNodesInCache";
-    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    public static final String CACHE_PERCENTAGE_RETRIEVED_FROM_CAHCE_HISTOGRAM =
-            "Accessibility.Android.Cache.PercentageRetrievedFromCache";
-    private static final int CACHE_MAX_NODES_MIN_BUCKET = 1;
-    private static final int CACHE_MAX_NODES_MAX_BUCKET = 3000;
-    private static final int CACHE_MAX_NODES_BUCKET_COUNT = 100;
-
     // Static instances of the two types of extra data keys that can be added to nodes.
     private static final List<String> sTextCharacterLocation =
             Collections.singletonList(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY);
@@ -239,6 +202,8 @@
     // Tracker for all actions performed and events sent by this instance, used for testing.
     private AccessibilityActionAndEventTracker mTracker;
 
+    private AccessibilityHistogramRecorder mHistogramRecorder;
+
     // Whether or not the next selection event should be fired. We only want to sent one traverse
     // and one selection event per granularity move, this ensures no double events while still
     // sending events when the user is using other assistive technology (e.g. external keyboard)
@@ -278,17 +243,6 @@
     private int mLastContentInvalidViewId;
     private long mLastContentInvalidUtteranceTime;
 
-    // These track the total number of enqueued events, and the total number of dispatched events,
-    // so we can report the percentage/number of dropped events.
-    private int mTotalEnqueuedEvents;
-    private int mTotalDispatchedEvents;
-
-    // These track the usage of the |mNodeInfoCache| to report metrics on the max number of items
-    // that were stored in the cache, and the percentage of requests retrieved from the cache.
-    private int mMaxNodesInCache;
-    private int mNodeWasReturnedFromCache;
-    private int mNodeWasCreatedFromScratch;
-
     // Set of all nodes that have received a request to populate image data. The request only needs
     // to be run once per node, and it completes asynchronously. We track which nodes have already
     // started the async request so that if downstream apps request the same node multiple times
@@ -398,6 +352,10 @@
                     }
                 }, eventThrottleDelays, viewIndependentEvents, new HashSet<Integer>(), false);
 
+        // Need to be initialized before AXTreeUpdate initialization because updateMaxNodesInCache
+        // gets called then
+        mHistogramRecorder = new AccessibilityHistogramRecorder();
+
         if (mDelegate.getNativeAXTree() != 0) {
             initializeNativeWithAXTreeUpdate(mDelegate.getNativeAXTree());
         }
@@ -522,12 +480,12 @@
 
     @VisibleForTesting
     public void forceRecordUMAHistogramsForTesting() {
-        recordUMAHistograms();
+        mHistogramRecorder.recordEventsHistograms();
     }
 
     @VisibleForTesting
     public void forceRecordCacheUMAHistogramsForTesting() {
-        recordCacheUMAHistograms();
+        mHistogramRecorder.recordCacheHistograms();
     }
 
     @VisibleForTesting
@@ -557,87 +515,7 @@
         mCaptioningController.stopListening();
         if (!isNativeInitialized()) return;
         ContextUtils.getApplicationContext().unregisterReceiver(mBroadcastReceiver);
-
-        // If the OnDemand feature is enabled, log UMA metrics and reset counters.
-        if (ContentFeatureList.isEnabled(ContentFeatureList.ON_DEMAND_ACCESSIBILITY_EVENTS)) {
-            recordUMAHistograms();
-        }
-
-        // Always track the histograms for cache usage statistics.
-        recordCacheUMAHistograms();
-    }
-
-    // Helper method to record UMA histograms for OnDemand feature and reset counters.
-    private void recordUMAHistograms() {
-        // To investigate whether adding more AXModes could be beneficial, track separate
-        // stats when both the ComputeAXMode and OnDemand features are enabled.
-        boolean isComputeAXModeEnabled =
-                ContentFeatureList.isEnabled(ContentFeatureList.COMPUTE_AX_MODE);
-        // There are only 2 AXModes, kAXModeComplete is used when a screenreader is active.
-        boolean isAXModeComplete = BrowserAccessibilityState.screenReaderMode();
-
-        // If we did not enqueue any events, we can ignore the data as a trivial case.
-        if (mTotalEnqueuedEvents > 0) {
-            // Log the percentage dropped (dispatching 0 events should be 100% dropped).
-            int percentSent = (int) (mTotalDispatchedEvents * 1.0 / mTotalEnqueuedEvents * 100.0);
-            RecordHistogram.recordPercentageHistogram(
-                    PERCENTAGE_DROPPED_HISTOGRAM, 100 - percentSent);
-            // Log the percentage dropped per AXMode as well.
-            if (isComputeAXModeEnabled) {
-                RecordHistogram.recordPercentageHistogram(isAXModeComplete
-                                ? PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_COMPLETE
-                                : PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_BASIC,
-                        100 - percentSent);
-            }
-
-            // Log the total number of dropped events. (Not relevant to be tracked per AXMode)
-            RecordHistogram.recordCustomCountHistogram(EVENTS_DROPPED_HISTOGRAM,
-                    mTotalEnqueuedEvents - mTotalDispatchedEvents,
-                    EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET, EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET,
-                    EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT);
-
-            // If 100% of events were dropped, also track the number of dropped events in a
-            // separate bucket.
-            if (percentSent == 0) {
-                RecordHistogram.recordCustomCountHistogram(ONE_HUNDRED_PERCENT_HISTOGRAM,
-                        mTotalEnqueuedEvents - mTotalDispatchedEvents,
-                        EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET, EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET,
-                        EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT);
-
-                // Log the 100% events count per AXMode as well.
-                if (isComputeAXModeEnabled) {
-                    RecordHistogram.recordCustomCountHistogram(isAXModeComplete
-                                    ? ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_COMPLETE
-                                    : ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_BASIC,
-                            mTotalEnqueuedEvents - mTotalDispatchedEvents,
-                            EVENTS_DROPPED_HISTOGRAM_MIN_BUCKET,
-                            EVENTS_DROPPED_HISTOGRAM_MAX_BUCKET,
-                            EVENTS_DROPPED_HISTOGRAM_BUCKET_COUNT);
-                }
-            }
-        }
-
-        // Reset counters.
-        mTotalEnqueuedEvents = 0;
-        mTotalDispatchedEvents = 0;
-    }
-
-    // Helper method to record UMA histograms for cache usage statistics.
-    private void recordCacheUMAHistograms() {
-        RecordHistogram.recordCustomCountHistogram(CACHE_MAX_NODES_HISTOGRAM, mMaxNodesInCache,
-                CACHE_MAX_NODES_MIN_BUCKET, CACHE_MAX_NODES_MAX_BUCKET,
-                CACHE_MAX_NODES_BUCKET_COUNT);
-
-        int totalNodeRequests = mNodeWasReturnedFromCache + mNodeWasCreatedFromScratch;
-        int percentFromCache = (int) (mNodeWasReturnedFromCache * 1.0 / totalNodeRequests * 100.0);
-
-        RecordHistogram.recordPercentageHistogram(
-                CACHE_PERCENTAGE_RETRIEVED_FROM_CAHCE_HISTOGRAM, percentFromCache);
-
-        // Reset counters.
-        mMaxNodesInCache = 0;
-        mNodeWasReturnedFromCache = 0;
-        mNodeWasCreatedFromScratch = 0;
+        mHistogramRecorder.recordHistograms();
     }
 
     @Override
@@ -753,7 +631,7 @@
 
     @CalledByNative
     public void updateMaxNodesInCache() {
-        mMaxNodesInCache = Math.max(mMaxNodesInCache, mNodeInfoCache.size());
+        mHistogramRecorder.updateMaxNodesInCache(mNodeInfoCache.size());
     }
 
     @CalledByNative
@@ -804,7 +682,7 @@
                     cachedNode.addAction(ACTION_ACCESSIBILITY_FOCUS);
                 }
 
-                mNodeWasReturnedFromCache++;
+                mHistogramRecorder.incrementNodeWasReturnedFromCache();
                 return cachedNode;
             } else {
                 // If the node is no longer valid, wipe it from the cache and return null
@@ -827,7 +705,7 @@
                         mNativeObj, info, virtualViewId)) {
                 // After successfully populating this node, add it to our cache then return.
                 mNodeInfoCache.put(virtualViewId, AccessibilityNodeInfoCompat.obtain(info));
-                mNodeWasCreatedFromScratch++;
+                mHistogramRecorder.incrementNodeWasCreatedFromScratch();
                 return info;
             } else {
                 info.recycle();
@@ -1516,7 +1394,7 @@
             return;
         }
 
-        mTotalEnqueuedEvents++;
+        mHistogramRecorder.incrementEnqueuedEvents();
         mEventDispatcher.enqueueEvent(virtualViewId, eventType);
     }
 
@@ -2080,7 +1958,7 @@
         // transiently null (such as during teardown, switching tabs...). Also ensure that
         // accessibility is still enabled, throttling may result in events sent late.
         if (mView.getParent() != null && isAccessibilityEnabled()) {
-            mTotalDispatchedEvents++;
+            mHistogramRecorder.incrementDispatchedEvents();
             if (mTracker != null) mTracker.addEvent(event);
             try {
                 mView.getParent().requestSendAccessibilityEvent(mView, event);
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
index 8c3889d..04f867b 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java
@@ -45,9 +45,15 @@
 import static org.chromium.content.browser.accessibility.AccessibilityContentShellTestUtils.sRangeInfoMatcher;
 import static org.chromium.content.browser.accessibility.AccessibilityContentShellTestUtils.sTextMatcher;
 import static org.chromium.content.browser.accessibility.AccessibilityContentShellTestUtils.sViewIdResourceNameMatcher;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.CACHE_MAX_NODES_HISTOGRAM;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.CACHE_PERCENTAGE_RETRIEVED_FROM_CAHCE_HISTOGRAM;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EVENTS_DROPPED_HISTOGRAM;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.CACHE_MAX_NODES_HISTOGRAM;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.CACHE_PERCENTAGE_RETRIEVED_FROM_CACHE_HISTOGRAM;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.EVENTS_DROPPED_HISTOGRAM;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.ONE_HUNDRED_PERCENT_HISTOGRAM;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_BASIC;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_COMPLETE;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.PERCENTAGE_DROPPED_HISTOGRAM;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_BASIC;
+import static org.chromium.content.browser.accessibility.AccessibilityHistogramRecorder.PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_COMPLETE;
 import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRAS_DATA_REQUEST_IMAGE_DATA_KEY;
 import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRAS_KEY_CHROME_ROLE;
 import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRAS_KEY_IMAGE_DATA;
@@ -57,12 +63,6 @@
 import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
 import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
 import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.ONE_HUNDRED_PERCENT_HISTOGRAM;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_BASIC;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.ONE_HUNDRED_PERCENT_HISTOGRAM_AXMODE_COMPLETE;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.PERCENTAGE_DROPPED_HISTOGRAM;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_BASIC;
-import static org.chromium.content.browser.accessibility.WebContentsAccessibilityImpl.PERCENTAGE_DROPPED_HISTOGRAM_AXMODE_COMPLETE;
 
 import android.annotation.SuppressLint;
 import android.content.ClipData;
@@ -92,6 +92,7 @@
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.UrlUtils;
@@ -113,6 +114,7 @@
  * implements the interface.
  */
 @RunWith(ContentJUnit4ClassRunner.class)
+@DoNotBatch(reason = "Flaky tests")
 @SuppressLint("VisibleForTests")
 public class WebContentsAccessibilityTest {
     // Test output error messages
@@ -556,7 +558,7 @@
                 RecordHistogram.getHistogramTotalCountForTesting(CACHE_MAX_NODES_HISTOGRAM));
         Assert.assertEquals(UMA_HISTOGRAM_ERROR, 1,
                 RecordHistogram.getHistogramTotalCountForTesting(
-                        CACHE_PERCENTAGE_RETRIEVED_FROM_CAHCE_HISTOGRAM));
+                        CACHE_PERCENTAGE_RETRIEVED_FROM_CACHE_HISTOGRAM));
     }
 
     /**