Avoid passive clipboard read on Android (#86791)

diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart
index 6c76b1e..5679989 100644
--- a/packages/flutter/lib/src/widgets/text_selection.dart
+++ b/packages/flutter/lib/src/widgets/text_selection.dart
@@ -1654,18 +1654,26 @@
 
   /// Check the [Clipboard] and update [value] if needed.
   Future<void> update() async {
-    // iOS 14 added a notification that appears when an app accesses the
-    // clipboard. To avoid the notification, don't access the clipboard on iOS,
-    // and instead always show the paste button, even when the clipboard is
-    // empty.
-    // TODO(justinmc): Use the new iOS 14 clipboard API method hasStrings that
-    // won't trigger the notification.
-    // https://github.com/flutter/flutter/issues/60145
     switch (defaultTargetPlatform) {
+      // Android 12 introduces a toast message that appears when an app reads
+      // the content on the clipboard. To avoid unintended access, both the
+      // Flutter engine and the Flutter framework need to be updated to use the
+      // appropriate API to check whether the clipboard is empty.
+      // As a short-term workaround, always show the paste button.
+      // TODO(justinmc): Expose `hasStrings` in `Clipboard` and use that instead
+      // https://github.com/flutter/flutter/issues/74139
+      case TargetPlatform.android:
+        // Intentionally fall through.
+      // iOS 14 added a notification that appears when an app accesses the
+      // clipboard. To avoid the notification, don't access the clipboard on iOS,
+      // and instead always show the paste button, even when the clipboard is
+      // empty.
+      // TODO(justinmc): Use the new iOS 14 clipboard API method hasStrings that
+      // won't trigger the notification.
+      // https://github.com/flutter/flutter/issues/60145
       case TargetPlatform.iOS:
         value = ClipboardStatus.pasteable;
         return;
-      case TargetPlatform.android:
       case TargetPlatform.fuchsia:
       case TargetPlatform.linux:
       case TargetPlatform.macOS:
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index f04e144..d1e5255 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -928,7 +928,7 @@
       ),
     );
     focusNode.requestFocus();
-    await tester.pump();
+    await tester.pumpAndSettle();
 
     await expectLater(
       find.byType(TextField),
@@ -956,7 +956,7 @@
       ),
     );
     focusNode.requestFocus();
-    await tester.pump();
+    await tester.pumpAndSettle();
 
     await expectLater(
       find.byType(TextField),
@@ -984,7 +984,7 @@
       ),
     );
     focusNode.requestFocus();
-    await tester.pump();
+    await tester.pumpAndSettle();
 
     await expectLater(
       find.byType(TextField),
@@ -6223,10 +6223,13 @@
 
     semantics.dispose();
 
-    // On web (just like iOS), we don't check for pasteability because that
-    // triggers a permission dialog in the browser.
+    // On web, we don't check for pasteability because that triggers a
+    // permission dialog in the browser.
     // https://github.com/flutter/flutter/pull/57139#issuecomment-629048058
-  }, skip: isBrowser);
+    // TODO(justinmc): Remove TargetPlatform override when Android and iOS uses
+    //                 `hasStrings` to check for pasteability.
+    //                 https://github.com/flutter/flutter/issues/74139
+  }, skip: isBrowser, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux }));
 
   testWidgets('TextField throws when not descended from a Material widget', (WidgetTester tester) async {
     const Widget textField = TextField();
@@ -9588,7 +9591,10 @@
       // pasted.
       expect(triedToReadClipboard, true);
     }
-  });
+    // TODO(justinmc): Eventually, all platform should call `hasStrings` instead.
+    //                 This entire test will become irrelevant after the fact.
+    //                 https://github.com/flutter/flutter/issues/74139
+  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux }));
 
   testWidgets('TextField changes mouse cursor when hovered', (WidgetTester tester) async {
     await tester.pumpWidget(
diff --git a/packages/flutter/test/material/text_selection_test.dart b/packages/flutter/test/material/text_selection_test.dart
index 950be89..3c48815 100644
--- a/packages/flutter/test/material/text_selection_test.dart
+++ b/packages/flutter/test/material/text_selection_test.dart
@@ -615,7 +615,9 @@
     });
   });
 
-  testWidgets('Paste only appears when clipboard has contents', (WidgetTester tester) async {
+  // TODO(justinmc): Paste should only appears when the clipboard has contents.
+  //                 https://github.com/flutter/flutter/issues/74139
+  testWidgets('Paste always appears regardless of clipboard content on Android', (WidgetTester tester) async {
     final TextEditingController controller = TextEditingController(
       text: 'Atwater Peel Sherbrooke Bonaventure',
     );
@@ -643,8 +645,10 @@
     await tester.tapAt(textOffsetToPosition(tester, index));
     await tester.pumpAndSettle();
 
-    // No Paste yet, because nothing has been copied.
-    expect(find.text('Paste'), findsNothing);
+    // Paste is showing even though clipboard is empty.
+    // TODO(justinmc): Paste should not appears when the clipboard is empty.
+    //                 https://github.com/flutter/flutter/issues/74139
+    expect(find.text('Paste'), findsOneWidget);
     expect(find.text('Copy'), findsOneWidget);
     expect(find.text('Cut'), findsOneWidget);
     expect(find.text('Select all'), findsOneWidget);
diff --git a/packages/flutter/test/widgets/selectable_text_test.dart b/packages/flutter/test/widgets/selectable_text_test.dart
index f3b9175..e58f680 100644
--- a/packages/flutter/test/widgets/selectable_text_test.dart
+++ b/packages/flutter/test/widgets/selectable_text_test.dart
@@ -2487,7 +2487,7 @@
     ));
 
     semanticsOwner.performAction(inputFieldId, SemanticsAction.longPress);
-    await tester.pump();
+    await tester.pumpAndSettle();
 
     expect(semantics, hasSemantics(
       TestSemantics.root(
diff --git a/packages/flutter/test/widgets/text_selection_test.dart b/packages/flutter/test/widgets/text_selection_test.dart
index 48107e8..283f563 100644
--- a/packages/flutter/test/widgets/text_selection_test.dart
+++ b/packages/flutter/test/widgets/text_selection_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:flutter/foundation.dart';
 import 'package:flutter/gestures.dart' show PointerDeviceKind, kSecondaryButton;
 import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
@@ -765,13 +766,19 @@
         TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null);
       });
 
-      test('Clipboard API failure is gracefully recovered from', () async {
+      // TODO(justinmc): See if `testWidgets` can be reverted to `test`.
+      testWidgets('Clipboard API failure is gracefully recovered from', (WidgetTester tester) async {
         final ClipboardStatusNotifier notifier = ClipboardStatusNotifier();
         expect(notifier.value, ClipboardStatus.unknown);
 
         await expectLater(notifier.update(), completes);
         expect(notifier.value, ClipboardStatus.unknown);
-      });
+        // TODO(justinmc): Currently on Android and iOS, ClipboardStatus.pasteable
+        //                 is always returned. Once both platforms properly use
+        //                 `hasStrings` to check whether the clipboard has any
+        //                 content, try to see if this override can be removed.
+        //                 https://github.com/flutter/flutter/issues/74139
+      }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux }));
     });
 
     group('when Clipboard succeeds', () {
@@ -785,7 +792,8 @@
         TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null);
       });
 
-      test('update sets value based on clipboard contents', () async {
+      // TODO(justinmc): See if `testWidgets` can be reverted to `test`.
+      testWidgets('update sets value based on clipboard contents', (WidgetTester tester) async {
         final ClipboardStatusNotifier notifier = ClipboardStatusNotifier();
         expect(notifier.value, ClipboardStatus.unknown);
 
@@ -800,7 +808,12 @@
         ));
         await expectLater(notifier.update(), completes);
         expect(notifier.value, ClipboardStatus.pasteable);
-      });
+        // TODO(justinmc): Currently on Android and iOS, ClipboardStatus.pasteable
+        //                 is always returned. Once both platforms properly use
+        //                 `hasStrings` to check whether the clipboard has any
+        //                 content, try to see if this override can be removed.
+        //                 https://github.com/flutter/flutter/issues/74139
+      }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux }));
     });
   });
 }