Skip to content

[Flutter Driver] The driver.tap() method right after application startup sometimes fails to open the keyboard on Android #163606

@harri35

Description

@harri35

Type of Request

Bug

Environment

Running the Linux_pixel_7pro integration_ui_keyboard_resize test on an Android device with the Flutter Driver

The goal of the test is: "Ensure keyboard dismissal resizes the view to original size" (eg not directly related to this bug)

What is happening?

Depending on the device executing the test and the device running the test, the following happens infrequently (but frequently enough to make the test flaky)

  1. The test launches the keyboard_resize test application using Flutter Driver in enableTextEntryEmulation: false mode
  2. The test waits for the defaultTextField TextField to be available via await driver.waitFor(defaultTextField);
  3. The test taps on the defaultTextField TextField via await driver.tap(defaultTextField);
  4. The defaultTextField TextField gets focus
  5. The Android keyboard is not opening. The test fails as it needs the keyboard open to proceed. The logs contain a warning from InputManager about not opening the keyboard:
W/InputMethodManager(20089): Ignoring showSoftInput() as view=io.flutter.embedding.android.FlutterView{636da98 VFE...... .F....ID 0,0-1080,2277 #1} is not served.

Expected: InputMethodManager opens the keyboard.

Video of this happening (runs the tests multiple times, fails at the last one): https://drive.google.com/file/d/1dQmIPs7mCVjxocCvWB9KSfsaSiB7_Tyh/view?usp=sharing

Easily reproducible on MacBook Pro M1 + Pixel 8 Pro API 36. Also, with Pixel 9 Pro XL.
In LUCY, it is happening on Pixel 7 Pro (API unknown)
Does not happen or happens very rarely on older Android devices. Not reproducible with Pixel 3.

Reproduce

NOTE: If the workaround in #162308 is already merged, then the test will not fail anymore, but logs will show the retries. If you prefer to debug with failure, then move the await driver.tap(defaultTextField); or use this infinite loop setup with additional logs: https://github.com/harri35/flutter/blob/fe80784cddb7dfdad1538cff26161abd34ec5e01/dev/integration_tests/ui/test_driver/keyboard_resize_test.dart#L41

  1. Disable automatic reboots: Comment out the await checkForRebootRequired(); line in
    } finally {
    await checkForRebootRequired();
    await forceQuitRunningProcesses();
    }
  2. Disable automatic retries: Set static const int retryNumber = 0; in
    /// Threshold to auto retry a failed test.
    static const int retryNumber = 2;
  3. Install hyperfine: This is a command-line benchmarking tool (e.g., brew install hyperfine on macOS).
  4. Connect a device/emulator: Connect a newer physical Android device. Use a direct cable between the computer and device, skip any slow USB hubs.
  5. Navigate to devicelab: Navigate to the flutter\dev\devicelab\ directory
  6. Run repeated tests: Use hyperfine to run the test multiple times and capture the output:
    hyperfine -r 2000 --style=full 'dart bin/test_runner.dart test -t integration_ui_keyboard_resize >> log.txt'
  1. Observe the logs:: Look for the W/InputMethodManager(...): Ignoring showSoftInput() ..

Note: If you use the setup with additional logs, then this command is useful on Mac:

grep -wo -F -i -E "(No|[0-9]+) retries were used to make this PASS\." log.txt | sort | uniq -c

Additional details

When running the test 2000x times, the following patterns can be observed.

Iterations it takes to open the keyboard if infinite retries are allowed:

MacBook Pro M1 + Pixel 8 Pro API 36 - at 2000/2000 successful tests

  569x "No retries were used to make this PASS."
 1428x "1 retries were used to make this PASS."
    1x "31 retries were used to make this PASS."
    1x "53 retries were used to make this PASS."
    1x "54 retries were used to make this PASS."

Logs have the following pattern - the keyboard can only be opened after the D/InsetsController(...): hide(ime(), fromIme=false) appears in the log.

...
<-Test starts->
.. end-to-end test Ensure keyboard dismissal resizes the view to original size ..
<- All attempts to open the keyboard fail in this period ->
D/InsetsController(...): hide(ime(), fromIme=false)
<- All attempts to open the keyboard succeed now ->
I/ImeTracker(...): com.yourcompany.integration_ui:...: onCancelled at PHASE_CLIENT_ALREADY_HIDDEN
I/ImeTracker(...): com.yourcompany.integration_ui:...: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
D/InsetsController(12023): show(ime(), fromIme=false)
<- Keyboard opens somewhere here .. ->
D/InsetsController(12023): Setting requestedVisibleTypes to -1 (was -9)
D/InputConnectionAdaptor(12023): The input method toggled cursor monitoring on
...

Looking a the timing,

  • In the tests with no retries or 1 retry the D/InsetsController(...): hide(ime(), fromIme=false) takes ~ 1 second to appear from start of the test
  • In the test with 54 retries it takes 21 seconds to appear

The D/InsetsController(...): hide(ime(), fromIme=false) log seems to be coming from https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/InsetsController.java;l=1208

Note: Using waitForTappable makes no difference.

All background and investigation

For all details and info, see

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projecta: text inputEntering text in a text field or keyboard related problemsfound in release: 3.30Found to occur in 3.30has reproducible stepsThe issue has been confirmed reproducible and is ready to work ont: flutter driver"flutter driver", flutter_drive, or a driver testteam-toolOwned by Flutter Tool teamtoolAffects the "flutter" command-line tool. See also t: labels.triaged-toolTriaged by Flutter Tool team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions