1.20 CP: fix FlutterViewUpdateCustomAccessibilityActions uses correct string list (#20238)

* fix FlutterViewUpdateCustomAccessibilityActions uses correct string list (#19623)

* fix FlutterViewUpdateCustomAccessibilityActions uses correct string list

* add test to ci

* format

* add license file

* fix test

* fix lint

* fix bad merge for BUILD.gn

* fix build.gn formatting

Co-authored-by: chunhtai <47866232+chunhtai@users.noreply.github.com>
diff --git a/BUILD.gn b/BUILD.gn
index edac93a..24c5925 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -86,6 +86,7 @@
         "//flutter/shell/common:shell_benchmarks",
         "//flutter/shell/platform/android/external_view_embedder:android_external_view_embedder_unittests",
         "//flutter/shell/platform/android/jni:jni_unittests",
+        "//flutter/shell/platform/android/platform_view_android_delegate:platform_view_android_delegate_unittests",
         "//flutter/third_party/txt:txt_benchmarks",
       ]
     }
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index bf97ba8..6cae668 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -801,6 +801,9 @@
 FILE: ../../../flutter/shell/platform/android/platform_message_response_android.h
 FILE: ../../../flutter/shell/platform/android/platform_view_android.cc
 FILE: ../../../flutter/shell/platform/android/platform_view_android.h
+FILE: ../../../flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.cc
+FILE: ../../../flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h
+FILE: ../../../flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate_unittests.cc
 FILE: ../../../flutter/shell/platform/android/platform_view_android_jni_impl.cc
 FILE: ../../../flutter/shell/platform/android/platform_view_android_jni_impl.h
 FILE: ../../../flutter/shell/platform/android/robolectric.properties
diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn
index 93e6b14..6d55b3c 100644
--- a/shell/platform/android/BUILD.gn
+++ b/shell/platform/android/BUILD.gn
@@ -64,6 +64,7 @@
     "//flutter/shell/platform/android/context",
     "//flutter/shell/platform/android/external_view_embedder",
     "//flutter/shell/platform/android/jni",
+    "//flutter/shell/platform/android/platform_view_android_delegate",
     "//flutter/shell/platform/android/surface",
     "//flutter/shell/platform/android/surface:native_window",
     "//third_party/skia",
diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc
index 9253e8e..5969946 100644
--- a/shell/platform/android/platform_view_android.cc
+++ b/shell/platform/android/platform_view_android.cc
@@ -52,7 +52,9 @@
     flutter::TaskRunners task_runners,
     std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
     bool use_software_rendering)
-    : PlatformView(delegate, std::move(task_runners)), jni_facade_(jni_facade) {
+    : PlatformView(delegate, std::move(task_runners)),
+      jni_facade_(jni_facade),
+      platform_view_android_delegate_(jni_facade) {
   std::shared_ptr<AndroidContext> android_context;
   if (use_software_rendering) {
     android_context =
@@ -81,7 +83,8 @@
     flutter::TaskRunners task_runners,
     std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
     : PlatformView(delegate, std::move(task_runners)),
-      jni_facade_(jni_facade) {}
+      jni_facade_(jni_facade),
+      platform_view_android_delegate_(jni_facade) {}
 
 PlatformViewAndroid::~PlatformViewAndroid() = default;
 
@@ -260,143 +263,7 @@
 void PlatformViewAndroid::UpdateSemantics(
     flutter::SemanticsNodeUpdates update,
     flutter::CustomAccessibilityActionUpdates actions) {
-  constexpr size_t kBytesPerNode = 41 * sizeof(int32_t);
-  constexpr size_t kBytesPerChild = sizeof(int32_t);
-  constexpr size_t kBytesPerAction = 4 * sizeof(int32_t);
-
-  {
-    size_t num_bytes = 0;
-    for (const auto& value : update) {
-      num_bytes += kBytesPerNode;
-      num_bytes +=
-          value.second.childrenInTraversalOrder.size() * kBytesPerChild;
-      num_bytes += value.second.childrenInHitTestOrder.size() * kBytesPerChild;
-      num_bytes +=
-          value.second.customAccessibilityActions.size() * kBytesPerChild;
-    }
-
-    // The encoding defined here is used in:
-    //
-    //  * AccessibilityBridge.java
-    //  * AccessibilityBridgeTest.java
-    //  * accessibility_bridge.mm
-    //
-    // If any of the encoding structure or length is changed, those locations
-    // must be updated (at a minimum).
-    std::vector<uint8_t> buffer(num_bytes);
-    int32_t* buffer_int32 = reinterpret_cast<int32_t*>(&buffer[0]);
-    float* buffer_float32 = reinterpret_cast<float*>(&buffer[0]);
-
-    std::vector<std::string> strings;
-    size_t position = 0;
-    for (const auto& value : update) {
-      // If you edit this code, make sure you update kBytesPerNode
-      // and/or kBytesPerChild above to match the number of values you are
-      // sending.
-      const flutter::SemanticsNode& node = value.second;
-      buffer_int32[position++] = node.id;
-      buffer_int32[position++] = node.flags;
-      buffer_int32[position++] = node.actions;
-      buffer_int32[position++] = node.maxValueLength;
-      buffer_int32[position++] = node.currentValueLength;
-      buffer_int32[position++] = node.textSelectionBase;
-      buffer_int32[position++] = node.textSelectionExtent;
-      buffer_int32[position++] = node.platformViewId;
-      buffer_int32[position++] = node.scrollChildren;
-      buffer_int32[position++] = node.scrollIndex;
-      buffer_float32[position++] = (float)node.scrollPosition;
-      buffer_float32[position++] = (float)node.scrollExtentMax;
-      buffer_float32[position++] = (float)node.scrollExtentMin;
-      if (node.label.empty()) {
-        buffer_int32[position++] = -1;
-      } else {
-        buffer_int32[position++] = strings.size();
-        strings.push_back(node.label);
-      }
-      if (node.value.empty()) {
-        buffer_int32[position++] = -1;
-      } else {
-        buffer_int32[position++] = strings.size();
-        strings.push_back(node.value);
-      }
-      if (node.increasedValue.empty()) {
-        buffer_int32[position++] = -1;
-      } else {
-        buffer_int32[position++] = strings.size();
-        strings.push_back(node.increasedValue);
-      }
-      if (node.decreasedValue.empty()) {
-        buffer_int32[position++] = -1;
-      } else {
-        buffer_int32[position++] = strings.size();
-        strings.push_back(node.decreasedValue);
-      }
-      if (node.hint.empty()) {
-        buffer_int32[position++] = -1;
-      } else {
-        buffer_int32[position++] = strings.size();
-        strings.push_back(node.hint);
-      }
-      buffer_int32[position++] = node.textDirection;
-      buffer_float32[position++] = node.rect.left();
-      buffer_float32[position++] = node.rect.top();
-      buffer_float32[position++] = node.rect.right();
-      buffer_float32[position++] = node.rect.bottom();
-      node.transform.getColMajor(&buffer_float32[position]);
-      position += 16;
-
-      buffer_int32[position++] = node.childrenInTraversalOrder.size();
-      for (int32_t child : node.childrenInTraversalOrder)
-        buffer_int32[position++] = child;
-
-      for (int32_t child : node.childrenInHitTestOrder)
-        buffer_int32[position++] = child;
-
-      buffer_int32[position++] = node.customAccessibilityActions.size();
-      for (int32_t child : node.customAccessibilityActions)
-        buffer_int32[position++] = child;
-    }
-
-    // custom accessibility actions.
-    size_t num_action_bytes = actions.size() * kBytesPerAction;
-    std::vector<uint8_t> actions_buffer(num_action_bytes);
-    int32_t* actions_buffer_int32 =
-        reinterpret_cast<int32_t*>(&actions_buffer[0]);
-
-    std::vector<std::string> action_strings;
-    size_t actions_position = 0;
-    for (const auto& value : actions) {
-      // If you edit this code, make sure you update kBytesPerAction
-      // to match the number of values you are
-      // sending.
-      const flutter::CustomAccessibilityAction& action = value.second;
-      actions_buffer_int32[actions_position++] = action.id;
-      actions_buffer_int32[actions_position++] = action.overrideId;
-      if (action.label.empty()) {
-        actions_buffer_int32[actions_position++] = -1;
-      } else {
-        actions_buffer_int32[actions_position++] = action_strings.size();
-        action_strings.push_back(action.label);
-      }
-      if (action.hint.empty()) {
-        actions_buffer_int32[actions_position++] = -1;
-      } else {
-        actions_buffer_int32[actions_position++] = action_strings.size();
-        action_strings.push_back(action.hint);
-      }
-    }
-
-    // Calling NewDirectByteBuffer in API level 22 and below with a size of zero
-    // will cause a JNI crash.
-    if (actions_buffer.size() > 0) {
-      jni_facade_->FlutterViewUpdateCustomAccessibilityActions(actions_buffer,
-                                                               strings);
-    }
-
-    if (buffer.size() > 0) {
-      jni_facade_->FlutterViewUpdateSemantics(buffer, strings);
-    }
-  }
+  platform_view_android_delegate_.UpdateSemantics(update, actions);
 }
 
 void PlatformViewAndroid::RegisterExternalTexture(
diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h
index f0c7bfa..34475dd 100644
--- a/shell/platform/android/platform_view_android.h
+++ b/shell/platform/android/platform_view_android.h
@@ -16,6 +16,7 @@
 #include "flutter/lib/ui/window/platform_message.h"
 #include "flutter/shell/common/platform_view.h"
 #include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
+#include "flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h"
 #include "flutter/shell/platform/android/surface/android_native_window.h"
 #include "flutter/shell/platform/android/surface/android_surface.h"
 
@@ -80,6 +81,8 @@
  private:
   const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
 
+  PlatformViewAndroidDelegate platform_view_android_delegate_;
+
   std::unique_ptr<AndroidSurface> android_surface_;
   // We use id 0 to mean that no response is expected.
   int next_response_id_ = 1;
diff --git a/shell/platform/android/platform_view_android_delegate/BUILD.gn b/shell/platform/android/platform_view_android_delegate/BUILD.gn
new file mode 100644
index 0000000..61f2019
--- /dev/null
+++ b/shell/platform/android/platform_view_android_delegate/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright 2013 The Flutter Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//flutter/common/config.gni")
+import("//flutter/testing/testing.gni")
+
+source_set("platform_view_android_delegate") {
+  sources = [
+    "platform_view_android_delegate.cc",
+    "platform_view_android_delegate.h",
+  ]
+
+  public_configs = [ "//flutter:config" ]
+
+  deps = [
+    "//flutter/common",
+    "//flutter/fml",
+    "//flutter/lib/ui",
+    "//flutter/shell/common",
+    "//flutter/shell/platform/android/jni",
+    "//third_party/skia",
+  ]
+}
+
+test_fixtures("platform_view_android_delegate_fixtures") {
+  fixtures = []
+}
+
+executable("platform_view_android_delegate_unittests") {
+  testonly = true
+
+  sources = [
+    "platform_view_android_delegate_unittests.cc",
+  ]
+
+  deps = [
+    ":platform_view_android_delegate",
+    ":platform_view_android_delegate_fixtures",
+    "//flutter/shell/platform/android/jni:jni_mock",
+    "//flutter/testing",
+    "//flutter/testing:dart",
+    "//flutter/third_party/txt",
+  ]
+}
diff --git a/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.cc b/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.cc
new file mode 100644
index 0000000..9570d72
--- /dev/null
+++ b/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.cc
@@ -0,0 +1,157 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h"
+
+namespace flutter {
+
+PlatformViewAndroidDelegate::PlatformViewAndroidDelegate(
+    std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
+    : jni_facade_(jni_facade){};
+
+void PlatformViewAndroidDelegate::UpdateSemantics(
+    flutter::SemanticsNodeUpdates update,
+    flutter::CustomAccessibilityActionUpdates actions) {
+  constexpr size_t kBytesPerNode = 41 * sizeof(int32_t);
+  constexpr size_t kBytesPerChild = sizeof(int32_t);
+  constexpr size_t kBytesPerAction = 4 * sizeof(int32_t);
+
+  {
+    size_t num_bytes = 0;
+    for (const auto& value : update) {
+      num_bytes += kBytesPerNode;
+      num_bytes +=
+          value.second.childrenInTraversalOrder.size() * kBytesPerChild;
+      num_bytes += value.second.childrenInHitTestOrder.size() * kBytesPerChild;
+      num_bytes +=
+          value.second.customAccessibilityActions.size() * kBytesPerChild;
+    }
+    // The encoding defined here is used in:
+    //
+    //  * AccessibilityBridge.java
+    //  * AccessibilityBridgeTest.java
+    //  * accessibility_bridge.mm
+    //
+    // If any of the encoding structure or length is changed, those locations
+    // must be updated (at a minimum).
+    std::vector<uint8_t> buffer(num_bytes);
+    int32_t* buffer_int32 = reinterpret_cast<int32_t*>(&buffer[0]);
+    float* buffer_float32 = reinterpret_cast<float*>(&buffer[0]);
+
+    std::vector<std::string> strings;
+    size_t position = 0;
+    for (const auto& value : update) {
+      // If you edit this code, make sure you update kBytesPerNode
+      // and/or kBytesPerChild above to match the number of values you are
+      // sending.
+      const flutter::SemanticsNode& node = value.second;
+      buffer_int32[position++] = node.id;
+      buffer_int32[position++] = node.flags;
+      buffer_int32[position++] = node.actions;
+      buffer_int32[position++] = node.maxValueLength;
+      buffer_int32[position++] = node.currentValueLength;
+      buffer_int32[position++] = node.textSelectionBase;
+      buffer_int32[position++] = node.textSelectionExtent;
+      buffer_int32[position++] = node.platformViewId;
+      buffer_int32[position++] = node.scrollChildren;
+      buffer_int32[position++] = node.scrollIndex;
+      buffer_float32[position++] = static_cast<float>(node.scrollPosition);
+      buffer_float32[position++] = static_cast<float>(node.scrollExtentMax);
+      buffer_float32[position++] = static_cast<float>(node.scrollExtentMin);
+      if (node.label.empty()) {
+        buffer_int32[position++] = -1;
+      } else {
+        buffer_int32[position++] = strings.size();
+        strings.push_back(node.label);
+      }
+      if (node.value.empty()) {
+        buffer_int32[position++] = -1;
+      } else {
+        buffer_int32[position++] = strings.size();
+        strings.push_back(node.value);
+      }
+      if (node.increasedValue.empty()) {
+        buffer_int32[position++] = -1;
+      } else {
+        buffer_int32[position++] = strings.size();
+        strings.push_back(node.increasedValue);
+      }
+      if (node.decreasedValue.empty()) {
+        buffer_int32[position++] = -1;
+      } else {
+        buffer_int32[position++] = strings.size();
+        strings.push_back(node.decreasedValue);
+      }
+      if (node.hint.empty()) {
+        buffer_int32[position++] = -1;
+      } else {
+        buffer_int32[position++] = strings.size();
+        strings.push_back(node.hint);
+      }
+      buffer_int32[position++] = node.textDirection;
+      buffer_float32[position++] = node.rect.left();
+      buffer_float32[position++] = node.rect.top();
+      buffer_float32[position++] = node.rect.right();
+      buffer_float32[position++] = node.rect.bottom();
+      node.transform.getColMajor(&buffer_float32[position]);
+      position += 16;
+
+      buffer_int32[position++] = node.childrenInTraversalOrder.size();
+      for (int32_t child : node.childrenInTraversalOrder) {
+        buffer_int32[position++] = child;
+      }
+
+      for (int32_t child : node.childrenInHitTestOrder) {
+        buffer_int32[position++] = child;
+      }
+
+      buffer_int32[position++] = node.customAccessibilityActions.size();
+      for (int32_t child : node.customAccessibilityActions) {
+        buffer_int32[position++] = child;
+      }
+    }
+
+    // custom accessibility actions.
+    size_t num_action_bytes = actions.size() * kBytesPerAction;
+    std::vector<uint8_t> actions_buffer(num_action_bytes);
+    int32_t* actions_buffer_int32 =
+        reinterpret_cast<int32_t*>(&actions_buffer[0]);
+
+    std::vector<std::string> action_strings;
+    size_t actions_position = 0;
+    for (const auto& value : actions) {
+      // If you edit this code, make sure you update kBytesPerAction
+      // to match the number of values you are
+      // sending.
+      const flutter::CustomAccessibilityAction& action = value.second;
+      actions_buffer_int32[actions_position++] = action.id;
+      actions_buffer_int32[actions_position++] = action.overrideId;
+      if (action.label.empty()) {
+        actions_buffer_int32[actions_position++] = -1;
+      } else {
+        actions_buffer_int32[actions_position++] = action_strings.size();
+        action_strings.push_back(action.label);
+      }
+      if (action.hint.empty()) {
+        actions_buffer_int32[actions_position++] = -1;
+      } else {
+        actions_buffer_int32[actions_position++] = action_strings.size();
+        action_strings.push_back(action.hint);
+      }
+    }
+
+    // Calling NewDirectByteBuffer in API level 22 and below with a size of zero
+    // will cause a JNI crash.
+    if (actions_buffer.size() > 0) {
+      jni_facade_->FlutterViewUpdateCustomAccessibilityActions(actions_buffer,
+                                                               action_strings);
+    }
+
+    if (buffer.size() > 0) {
+      jni_facade_->FlutterViewUpdateSemantics(buffer, strings);
+    }
+  }
+}
+
+}  // namespace flutter
diff --git a/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h b/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h
new file mode 100644
index 0000000..3f76f34
--- /dev/null
+++ b/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_DELEGATE_H_
+#define SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_DELEGATE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "flutter/shell/common/platform_view.h"
+#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
+
+namespace flutter {
+
+class PlatformViewAndroidDelegate {
+ public:
+  PlatformViewAndroidDelegate(
+      std::shared_ptr<PlatformViewAndroidJNI> jni_facade);
+  void UpdateSemantics(flutter::SemanticsNodeUpdates update,
+                       flutter::CustomAccessibilityActionUpdates actions);
+
+ private:
+  const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
+};
+}  // namespace flutter
+
+#endif  // SHELL_PLATFORM_ANDROID_PLATFORM_VIEW_ANDROID_H_
diff --git a/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate_unittests.cc b/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate_unittests.cc
new file mode 100644
index 0000000..56371ff
--- /dev/null
+++ b/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate_unittests.cc
@@ -0,0 +1,96 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/android/jni/jni_mock.h"
+#include "flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace flutter {
+namespace testing {
+
+TEST(PlatformViewShell, UpdateSemanticsDoesFlutterViewUpdateSemantics) {
+  auto jni_mock = std::make_shared<JNIMock>();
+  auto delegate = std::make_unique<PlatformViewAndroidDelegate>(jni_mock);
+
+  flutter::SemanticsNodeUpdates update;
+  flutter::SemanticsNode node0;
+  node0.id = 0;
+  node0.label = "label";
+  update.insert(std::make_pair(0, std::move(node0)));
+
+  std::vector<uint8_t> expected_buffer(164);
+  size_t position = 0;
+  int32_t* buffer_int32 = reinterpret_cast<int32_t*>(&expected_buffer[0]);
+  float* buffer_float32 = reinterpret_cast<float*>(&expected_buffer[0]);
+  std::vector<std::string> expected_strings;
+  buffer_int32[position++] = node0.id;
+  buffer_int32[position++] = node0.flags;
+  buffer_int32[position++] = node0.actions;
+  buffer_int32[position++] = node0.maxValueLength;
+  buffer_int32[position++] = node0.currentValueLength;
+  buffer_int32[position++] = node0.textSelectionBase;
+  buffer_int32[position++] = node0.textSelectionExtent;
+  buffer_int32[position++] = node0.platformViewId;
+  buffer_int32[position++] = node0.scrollChildren;
+  buffer_int32[position++] = node0.scrollIndex;
+  buffer_float32[position++] = static_cast<float>(node0.scrollPosition);
+  buffer_float32[position++] = static_cast<float>(node0.scrollExtentMax);
+  buffer_float32[position++] = static_cast<float>(node0.scrollExtentMin);
+  buffer_int32[position++] = expected_strings.size();  // node0.label
+  expected_strings.push_back(node0.label);
+  buffer_int32[position++] = -1;  // node0.value
+  buffer_int32[position++] = -1;  // node0.increasedValue
+  buffer_int32[position++] = -1;  // node0.decreasedValue
+  buffer_int32[position++] = -1;  // node0.hint
+  buffer_int32[position++] = node0.textDirection;
+  buffer_float32[position++] = node0.rect.left();
+  buffer_float32[position++] = node0.rect.top();
+  buffer_float32[position++] = node0.rect.right();
+  buffer_float32[position++] = node0.rect.bottom();
+  node0.transform.getColMajor(&buffer_float32[position]);
+  position += 16;
+  buffer_int32[position++] = 0;  // node0.childrenInTraversalOrder.size();
+  buffer_int32[position++] = 0;  // node0.customAccessibilityActions.size();
+
+  EXPECT_CALL(*jni_mock,
+              FlutterViewUpdateSemantics(expected_buffer, expected_strings));
+  // Creates empty custom actions.
+  flutter::CustomAccessibilityActionUpdates actions;
+  delegate->UpdateSemantics(update, actions);
+}
+
+TEST(PlatformViewShell,
+     UpdateSemanticsDoesFlutterViewUpdateCustomAccessibilityActions) {
+  auto jni_mock = std::make_shared<JNIMock>();
+  auto delegate = std::make_unique<PlatformViewAndroidDelegate>(jni_mock);
+
+  flutter::CustomAccessibilityActionUpdates actions;
+  flutter::CustomAccessibilityAction action0;
+  action0.id = 0;
+  action0.overrideId = 1;
+  action0.label = "label";
+  action0.hint = "hint";
+  actions.insert(std::make_pair(0, std::move(action0)));
+
+  std::vector<uint8_t> expected_actions_buffer(16);
+  int32_t* actions_buffer_int32 =
+      reinterpret_cast<int32_t*>(&expected_actions_buffer[0]);
+  std::vector<std::string> expected_action_strings;
+  actions_buffer_int32[0] = action0.id;
+  actions_buffer_int32[1] = action0.overrideId;
+  actions_buffer_int32[2] = expected_action_strings.size();
+  expected_action_strings.push_back(action0.label);
+  actions_buffer_int32[3] = expected_action_strings.size();
+  expected_action_strings.push_back(action0.hint);
+
+  EXPECT_CALL(*jni_mock, FlutterViewUpdateCustomAccessibilityActions(
+                             expected_actions_buffer, expected_action_strings));
+  // Creates empty update.
+  flutter::SemanticsNodeUpdates update;
+  delegate->UpdateSemantics(update, actions);
+}
+
+}  // namespace testing
+}  // namespace flutter
diff --git a/testing/run_tests.py b/testing/run_tests.py
index b799030..bdad851 100755
--- a/testing/run_tests.py
+++ b/testing/run_tests.py
@@ -137,6 +137,7 @@
     # https://github.com/google/googletest/issues/2490
     RunEngineExecutable(build_dir, 'android_external_view_embedder_unittests', filter, shuffle_flags)
     RunEngineExecutable(build_dir, 'jni_unittests', filter, shuffle_flags)
+    RunEngineExecutable(build_dir, 'platform_view_android_delegate_unittests', filter, shuffle_flags)
 
   RunEngineExecutable(build_dir, 'ui_unittests', filter, shuffle_flags)