v1.12.13+hotfix.7 cherry-picks (#49437)

* flutter/engine@a677925 Cherry pick infra testing updates into hotfixes
* flutter/engine@b1bb773 Raise API level for reportFullyDrawn
* flutter/engine@a433ed9 v1.12.13+hotfix.7 cherry-picks
  * google/skia@a640745 Disable QCOM_tiled_rendering while we wait for test devices
  * flutter/engine@bcb8267 Revert "Do not default to downstream affinity on iOS insertText"
* d345a3b Revert "Track and use fallback TextAffinity for null affinity platform TextSelections. (#44622)"
* bd25f70 Upgrade dartdoc to 0.29.3
* 7915e58 Change firebase Pixel3 version from Q-beta3 to 29
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index cf062e0..ea11d61 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-2994f7e1e682039464cb25e31a78b86a3c59b695
+a67792536ca236a971d0efbcfd7af4efb8f6c119
diff --git a/dev/bots/docs.sh b/dev/bots/docs.sh
index 3df24c8..664c3f8 100755
--- a/dev/bots/docs.sh
+++ b/dev/bots/docs.sh
@@ -107,7 +107,7 @@
 fi
 
 # Install and activate dartdoc.
-"$PUB" global activate dartdoc 0.29.1
+"$PUB" global activate dartdoc 0.29.3
 
 # This script generates a unified doc set, and creates
 # a custom index.html, placing everything into dev/docs/doc.
diff --git a/dev/bots/firebase_testlab.sh b/dev/bots/firebase_testlab.sh
index 98f1020..afda57b 100755
--- a/dev/bots/firebase_testlab.sh
+++ b/dev/bots/firebase_testlab.sh
@@ -18,7 +18,7 @@
 devices=(
   # Pixel 3
   "model=blueline,version=28"
-  "model=blueline,version=Q-beta-3"
+  "model=blueline,version=29"
 
   # Moto Z XT1650
   "model=griffin,version=24"
diff --git a/packages/flutter/lib/src/painting/text_painter.dart b/packages/flutter/lib/src/painting/text_painter.dart
index 390920b..95bfb87 100644
--- a/packages/flutter/lib/src/painting/text_painter.dart
+++ b/packages/flutter/lib/src/painting/text_painter.dart
@@ -819,14 +819,9 @@
     return _paragraph.getWordBoundary(position);
   }
 
-  /// Returns the [TextRange] of the line at the given [TextPosition].
+  /// Returns the text range of the line at the given offset.
   ///
-  /// The newline, if any, is returned as part of the range.
-  ///
-  /// Not valid until after layout.
-  ///
-  /// This can potentially be expensive, since it needs to compute the full
-  /// layout before it is available.
+  /// The newline, if any, is included in the range.
   TextRange getLineBoundary(TextPosition position) {
     assert(!_needsLayout);
     return _paragraph.getLineBoundary(position);
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
index ad76598..f61c82c 100644
--- a/packages/flutter/lib/src/rendering/editable.dart
+++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -402,12 +402,6 @@
   // down in a multiline text field when selecting using the keyboard.
   bool _wasSelectingVerticallyWithKeyboard = false;
 
-  // This is the affinity we use when a platform-supplied value has null
-  // affinity.
-  //
-  // This affinity should never be null.
-  TextAffinity _fallbackAffinity = TextAffinity.downstream;
-
   // Call through to onSelectionChanged.
   void _handleSelectionChange(
     TextSelection nextSelection,
@@ -424,17 +418,6 @@
     }
   }
 
-  // Sets the fallback affinity to the affinity of the selection.
-  void _setFallbackAffinity(
-    TextAffinity affinity,
-  ) {
-    assert(affinity != null);
-    // Engine-computed selections will always compute affinity when necessary.
-    // Cache this affinity in the case where the platform supplied selection
-    // does not provide an affinity.
-    _fallbackAffinity = affinity;
-  }
-
   static final Set<LogicalKeyboardKey> _movementKeys = <LogicalKeyboardKey>{
     LogicalKeyboardKey.arrowRight,
     LogicalKeyboardKey.arrowLeft,
@@ -980,15 +963,7 @@
   set selection(TextSelection value) {
     if (_selection == value)
       return;
-    // Use the _fallbackAffinity when the set selection has a null
-    // affinity. This happens when the platform does not supply affinity,
-    // in which case using the fallback affinity computed from dart:ui will
-    // be superior to simply defaulting to TextAffinity.downstream.
-    if (value.affinity == null) {
-      _selection = value.copyWith(affinity: _fallbackAffinity);
-    } else {
-      _selection = value;
-    }
+    _selection = value;
     _selectionRects = null;
     markNeedsPaint();
     markNeedsSemanticsUpdate();
@@ -1591,7 +1566,6 @@
     );
     // Call [onSelectionChanged] only when the selection actually changed.
     _handleSelectionChange(newSelection, cause);
-    _setFallbackAffinity(newSelection.affinity);
   }
 
   /// Select a word around the location of the last tap down.
@@ -1640,18 +1614,15 @@
       return;
     }
     final TextPosition position = _textPainter.getPositionForOffset(globalToLocal(_lastTapDownPosition - _paintOffset));
-    _setFallbackAffinity(position.affinity);
     final TextRange word = _textPainter.getWordBoundary(position);
-    final TextRange lineBoundary = _textPainter.getLineBoundary(position);
-    final bool endOfLine = lineBoundary?.end == position.offset && position.affinity != null;
     if (position.offset - word.start <= 1) {
       _handleSelectionChange(
-        TextSelection.collapsed(offset: word.start, affinity: endOfLine ? position.affinity : TextAffinity.downstream),
+        TextSelection.collapsed(offset: word.start, affinity: TextAffinity.downstream),
         cause,
       );
     } else {
       _handleSelectionChange(
-        TextSelection.collapsed(offset: word.end, affinity: endOfLine ? position.affinity : TextAffinity.upstream),
+        TextSelection.collapsed(offset: word.end, affinity: TextAffinity.upstream),
         cause,
       );
     }
diff --git a/packages/flutter/lib/src/services/text_editing.dart b/packages/flutter/lib/src/services/text_editing.dart
index 5218034..1fb13c9 100644
--- a/packages/flutter/lib/src/services/text_editing.dart
+++ b/packages/flutter/lib/src/services/text_editing.dart
@@ -81,7 +81,7 @@
   /// The position at which the selection originates.
   ///
   /// Might be larger than, smaller than, or equal to extent.
-  TextPosition get base => TextPosition(offset: baseOffset, affinity: affinity ?? TextAffinity.downstream);
+  TextPosition get base => TextPosition(offset: baseOffset, affinity: affinity);
 
   /// The position at which the selection terminates.
   ///
@@ -90,7 +90,7 @@
   /// side of the selection, this is the location at which to paint the caret.
   ///
   /// Might be larger than, smaller than, or equal to base.
-  TextPosition get extent => TextPosition(offset: extentOffset, affinity: affinity ?? TextAffinity.downstream);
+  TextPosition get extent => TextPosition(offset: extentOffset, affinity: affinity);
 
   @override
   String toString() {
diff --git a/packages/flutter/lib/src/services/text_input.dart b/packages/flutter/lib/src/services/text_input.dart
index 2483b44..b39acf3 100644
--- a/packages/flutter/lib/src/services/text_input.dart
+++ b/packages/flutter/lib/src/services/text_input.dart
@@ -472,9 +472,6 @@
     case 'TextAffinity.upstream':
       return TextAffinity.upstream;
   }
-  // Null affinity indicates that the platform did not provide a valid
-  // affinity. Set it to null here to allow the framework to supply
-  // a fallback affinity.
   return null;
 }
 
@@ -536,7 +533,7 @@
       selection: TextSelection(
         baseOffset: encoded['selectionBase'] ?? -1,
         extentOffset: encoded['selectionExtent'] ?? -1,
-        affinity: _toTextAffinity(encoded['selectionAffinity']),
+        affinity: _toTextAffinity(encoded['selectionAffinity']) ?? TextAffinity.downstream,
         isDirectional: encoded['selectionIsDirectional'] ?? false,
       ),
       composing: TextRange(
diff --git a/packages/flutter/test/painting/text_painter_test.dart b/packages/flutter/test/painting/text_painter_test.dart
index 67c8fcc..9175d80 100644
--- a/packages/flutter/test/painting/text_painter_test.dart
+++ b/packages/flutter/test/painting/text_painter_test.dart
@@ -790,42 +790,4 @@
     expect(lines[2].lineNumber, 2);
     expect(lines[3].lineNumber, 3);
   }, skip: !isLinux);
-
-  test('getLineBoundary', () {
-    final TextPainter painter = TextPainter()
-      ..textDirection = TextDirection.ltr;
-
-    const String text = 'test1\nhello line two really long for soft break\nfinal line 4';
-    painter.text = const TextSpan(
-      text: text,
-    );
-
-    painter.layout(maxWidth: 300);
-
-    final List<ui.LineMetrics> lines = painter.computeLineMetrics();
-
-    expect(lines.length, 4);
-
-    expect(painter.getLineBoundary(const TextPosition(offset: -1)), const TextRange(start: -1, end: -1));
-
-    expect(painter.getLineBoundary(const TextPosition(offset: 0)), const TextRange(start: 0, end: 5));
-    expect(painter.getLineBoundary(const TextPosition(offset: 1)), const TextRange(start: 0, end: 5));
-    expect(painter.getLineBoundary(const TextPosition(offset: 4)), const TextRange(start: 0, end: 5));
-    expect(painter.getLineBoundary(const TextPosition(offset: 5)), const TextRange(start: 0, end: 5));
-
-    expect(painter.getLineBoundary(const TextPosition(offset: 10)), const TextRange(start: 6, end: 28));
-    expect(painter.getLineBoundary(const TextPosition(offset: 15)), const TextRange(start: 6, end: 28));
-    expect(painter.getLineBoundary(const TextPosition(offset: 21)), const TextRange(start: 6, end: 28));
-    expect(painter.getLineBoundary(const TextPosition(offset: 28)), const TextRange(start: 6, end: 28));
-
-    expect(painter.getLineBoundary(const TextPosition(offset: 29)), const TextRange(start: 28, end: 47));
-    expect(painter.getLineBoundary(const TextPosition(offset: 47)), const TextRange(start: 28, end: 47));
-
-    expect(painter.getLineBoundary(const TextPosition(offset: 48)), const TextRange(start: 48, end: 60));
-    expect(painter.getLineBoundary(const TextPosition(offset: 49)), const TextRange(start: 48, end: 60));
-    expect(painter.getLineBoundary(const TextPosition(offset: 60)), const TextRange(start: 48, end: 60));
-
-    expect(painter.getLineBoundary(const TextPosition(offset: 61)), const TextRange(start: -1, end: -1));
-    expect(painter.getLineBoundary(const TextPosition(offset: 100)), const TextRange(start: -1, end: -1));
-  }, skip: !isLinux);
 }
diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart
index 3a78b14..3c74861 100644
--- a/packages/flutter/test/rendering/editable_test.dart
+++ b/packages/flutter/test/rendering/editable_test.dart
@@ -611,29 +611,4 @@
     editable.layout(BoxConstraints.loose(const Size(1000.0, 1000.0)));
     expect(editable.maxScrollExtent, equals(10));
   }, skip: isBrowser); // TODO(yjbanov): https://github.com/flutter/flutter/issues/42772
-
-  test('selection affinity uses fallback', () {
-    final TextSelectionDelegate delegate = FakeEditableTextState();
-    EditableText.debugDeterministicCursor = true;
-
-    final RenderEditable editable = RenderEditable(
-      textDirection: TextDirection.ltr,
-      cursorColor: const Color.fromARGB(0xFF, 0xFF, 0x00, 0x00),
-      offset: ViewportOffset.zero(),
-      textSelectionDelegate: delegate,
-      startHandleLayerLink: LayerLink(),
-      endHandleLayerLink: LayerLink(),
-    );
-
-    expect(editable.selection, null);
-
-    const TextSelection sel1 = TextSelection(baseOffset: 10, extentOffset: 11);
-    editable.selection = sel1;
-    expect(editable.selection, sel1);
-
-    const TextSelection sel2 = TextSelection(baseOffset: 10, extentOffset: 11, affinity: null);
-    const TextSelection sel3 = TextSelection(baseOffset: 10, extentOffset: 11, affinity: TextAffinity.downstream);
-    editable.selection = sel2;
-    expect(editable.selection, sel3);
-  }, skip: isBrowser);
 }
diff --git a/packages/flutter/test/services/text_input_test.dart b/packages/flutter/test/services/text_input_test.dart
index daea4f4..f5fcd02 100644
--- a/packages/flutter/test/services/text_input_test.dart
+++ b/packages/flutter/test/services/text_input_test.dart
@@ -173,37 +173,6 @@
       expect(client.latestMethodCall, 'connectionClosed');
     });
   });
-
-  test('TextEditingValue handles JSON affinity', () async {
-    final Map<String, dynamic> json = <String, dynamic>{};
-    json['text'] = 'Xiaomuqiao';
-
-    TextEditingValue val = TextEditingValue.fromJSON(json);
-    expect(val.text, 'Xiaomuqiao');
-    expect(val.selection.baseOffset, -1);
-    expect(val.selection.extentOffset, -1);
-    expect(val.selection.affinity, null);
-    expect(val.selection.isDirectional, false);
-    expect(val.composing.start, -1);
-    expect(val.composing.end, -1);
-
-    json['text'] = 'Xiaomuqiao';
-    json['selectionBase'] = 5;
-    json['selectionExtent'] = 6;
-    json['selectionAffinity'] = 'TextAffinity.upstream';
-    json['selectionIsDirectional'] = true;
-    json['composingBase'] = 7;
-    json['composingExtent'] = 8;
-
-    val = TextEditingValue.fromJSON(json);
-    expect(val.text, 'Xiaomuqiao');
-    expect(val.selection.baseOffset, 5);
-    expect(val.selection.extentOffset, 6);
-    expect(val.selection.affinity, TextAffinity.upstream);
-    expect(val.selection.isDirectional, true);
-    expect(val.composing.start, 7);
-    expect(val.composing.end, 8);
-  });
 }
 
 class FakeTextInputClient implements TextInputClient {