Neo键盘无法弹出记录

Flutter Platform View 相关资料

TODO
尝试一下 Flutter Platform View 的 HCPP,一个页面加载多个 Platform View,能不能切换层架,平台视图是 SurfaceView 的情况

Flutter 对虚拟显示器的处理方式

Virtual-Display.md

Flutter 和 Neo 在虚拟显示器的使用其实是不同的,Flutter 借助的是 Presentation 通过 presentation.show() 将 View 显示到虚拟显示器

在这个文档中,Flutter 解释了这个方式实现的一些复杂性,所以目前更多的是使用 Hy

在最新的文档中 https://docs.flutter.dev/platform-integration/android/platform-views, Flutter 已经不把 Virtual Display 当做推荐的使用方式了

Performance

Flutter 文档对这两种的性能解释

1
2
3
4
5
6
7
8
9
10
11
12
13
Platform views in Flutter come with performance trade-offs.

For example, in a typical Flutter app, the Flutter UI is composed on a dedicated raster thread. This allows Flutter apps to be fast, as the main platform thread is rarely blocked.

While a platform view is rendered with hybrid composition, the Flutter UI is composed from the platform thread, which competes with other tasks like handling OS or plugin messages.

Prior to Android 10, hybrid composition copied each Flutter frame out of the graphic memory into main memory, and then copied it back to a GPU texture. As this copy happens per frame, the performance of the entire Flutter UI might be impacted. In Android 10 or above, the graphics memory is copied only once.

Virtual display, on the other hand, makes each pixel of the native view flow through additional intermediate graphic buffers, which cost graphic memory and drawing performance.

For complex cases, there are some techniques that can be used to mitigate these issues.

For example, you could use a placeholder texture while an animation is happening in Dart. In other words, if an animation is slow while a platform view is rendered, then consider taking a screenshot of the native view and rendering it as a texture.

Hybrid Composition

Platform Views are rendered as they are normally. Flutter content is rendered into a texture. SurfaceFlinger composes the Flutter content and the platform views.

  • best performance and fidelity of Android views.
  • Flutter performance suffers.
  • FPS of application will be lower.
  • Certain transformations that can be applied to Flutter widgets will not work when applied to platform views.

Texture Layer (or Texture Layer Hybrid Composition)

Platform Views are rendered into a texture. Flutter draws the platform views (via the texture). Flutter content is rendered directly into a Surface.

  • good performance for Android Views
  • best performance for Flutter rendering.
  • all transformations work correctly.
  • quick scrolling (e.g. a web view) will be janky
  • SurfaceViews are problematic in this mode and will be moved into a virtual display (breaking a11y)
  • Text magnifier will break unless Flutter is rendered into a TextureView.

Scrcpy 可以将输入法移动到虚拟显示器的实现方案

1
getWindowManager().setDisplayImePolicy(virtualDisplayId, displayImePolicy);

这个 displayImePolicy 是0, 也就是说某个显示器标记为0则会在这个显示器上展示键盘,如果是1,则会在主显示器上展示键盘

触控板反馈

1.触控板中点进程序的输入窗口,输入法(小键盘)只能被唤醒一次,输入一次以后,再点输入框就没反应了
2.双指触碰触控板以后(本来是为了退出全屏),鼠标的光标消失了,要大退才能重新用。这个问题不是每次都是这样,有时是正常的,但是出bug的频率蛮高的

测试结果

  • 1.Flutter App 通过 Neo 打开,点击输入框无法打开键盘 ❌
  • 2.其他 App 通过 Neo 打开,能打开键盘 ✅
  • 3.ADB KIT Debug 模式,点输入框,键盘能弹,Release 模式不能弹,Neo Resize 窗口后,点击又能弹一次 ❌
  • 4.用 scrcpy 显示对应的 display,点击输入框能弹 ✅
  • 5.用 adb shell input -d 147 tap 300 300,点击 Neo 内的 Flutter App(ADB KIT),能弹(关键) ✅

其中3是比较诡异的,猜测是 VD 触发 resize 后,焦点自动切换到了窗口内的 task 上

先理清下思路

在 Neo 的窗口中,渲染的是 Flutter App 还是其他的 App,弹出键盘的行为有差异

可能是 Flutter App 的一些处理导致的,而且很诡异的,Debug 模式的 Flutter App,键盘都能正常弹出,猜想是 Debug 运行的代码执行比较慢,在很玄学的层面,绕开了焦点在多个显示器间切换的问题

Flutter 的虚拟显示器没有焦点,是因为它是普通的应用权限,无法添加VIRTUAL_DISPLAY_FLAG_OWN_FOCUS flag,而 Neo 是 shell 权限,所以并不是 VD 没有焦点,而是焦点不在期望的显示器上

使用 scrcpy 投屏 Neo 中的窗口,点击 Flutter App 输入框,键盘是能弹出的,这个跟 ime_policy 还没关系

测试5验证了这一点

因为此时在于 VD 内的应用交互的时候,并没有同时点击设备的主屏幕,所以此时的焦点就在 VD 上

结论: Neo 点击应用内窗口无法弹出软键盘,是因为事件同时在以下几个地方生效
1.手指正在触摸主屏幕,无论是按下、移动、抬起,Activity 中的 View(FlutterView) 在被点击的时候,就会申请焦点
FlutterView.java#L384C1-L386C35

1
2
3
// FlutterView needs to be focusable so that the InputMethodManager can interact with it.
setFocusable(true);
setFocusableInTouchMode(true);

2.Neo 将这些事件同时下发到了 VD 中

此时的焦点应该会来回在多个显示器间切换

TODO 用 AAS 验证一下

ACTION_DOWN -> FlutterView(Focus) -> InjectEvent(VD) -> VD’s Task(Focus)

1
adb logcat -v time | grep --line-buffered -E "InputMethodManagerService|InputMethodManager|onRequestShow|SHOW_SOFT_INPUT|setImeWindowStatus|displayId|InputDispatcher|WindowManager"

仍然很诡异

下面是使用 Neo 在内置显示器上点击 ADBKIT 的窗口内的输入框

  • Android 16 软键盘 ✅
  • Android 15 软键盘 ✅
  • Android 14 软键盘 ✅
  • Android 13 软键盘 ✅

使用外置显示器,通过触控板,点击 ADB KIT 窗口内的输入框

  • Android 16 软键盘 ❌
  • Android 15 软键盘 ❌
  • Android 14 软键盘 ❌
  • Android 13 软键盘 ❌

此时用 scrcpy 直接操作 ADB KIT 所在的显示器,点击输入框

  • Android 15 软键盘 ✅

猜测还是焦点导致的

鼠标消失

在 Android 13 模拟器上测试,第二次打开 Neo,鼠标消失,原因是软件其实并没有完全退出,怎么解?

所以只在我一加 Pad2 Pro 上出现?

Neo 点击 Flutter App 日志

1
2
E/WindowManager(3097): isInputMethodClientFocus: display ID mismatch. from client: 106 from window: 0
W/InputMethodManagerService(3097): Ignoring showSoftInput of uid 10315

IME(输入法服务)判断请求来源于 display 106,
而它要附着(attach)的窗口 token 属于 display 0(主屏)。
系统检查不通过,于是直接忽略。

scrcpy 点击 Flutter App 日志

scrcpy server 在 该 displayId=106 上注入事件;

同时 焦点窗口(WindowState)与 IME client 的 displayId 一致(都为 106);

因此 isInputMethodClientFocus() 检查通过,键盘可显示。

1
2
E/WindowManager: isInputMethodClientFocus: display ID mismatch. from client: 111 from window: 0  
W/InputMethodManagerService: Ignoring showSoftInput of uid 10288

Neo 点击系统 App 日志,猜测是其他的 App 在唤起软键盘和 Flutter 是不一样的方式,所以在焦点不对的情况下,键盘也能弹出

1
2
3
V/InputMethodManager: Display ID mismatch found.
ViewRootImpl displayId=111 InputMethodManager displayId=0.
Use the right InputMethodManager instance to avoid performance overhead.

事件原理分析

1
2
3
4
void onRequestShow(InputMethodClient client, int reason, boolean fromUser) {
Slog.v(TAG, client + ": onRequestShow at " + origin + " reason " + reasonToString(reason)
+ " fromUser " + fromUser);
}

在 Neo 中点击窗口内应用的输入框

1
com.nightmare.neo:f85ed5fc: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false

程序调用的代码

1
InputMethodManager.showSoftInput(view, 0);

AAS 测试结果

通过在一加 Pad2 Pro 上点击 Neo 内部的 ADB KIT 输入框,键盘无法弹出

AAS 的 onTaskFocusChanged 并未回调,也就是说虽然通过 InjectEvent 注入了点击事件,但是焦点并没有切换到 VD 上(很奇怪)

onTaskStackChanged 关键

代码调用 VD Resize 并不触发 onTaskStackChanged

Android 15(模拟器)

点击 ADB KIT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
· onRecentTaskListUpdated
· onTaskCreated: taskId=36, componentName=null
· onTaskDisplayChanged: taskId=36, newDisplayId=3
· onTaskCreated: taskId=36, componentName=ComponentInfo{com.nightmare.adbkit/com.nightmare.adbkit.MainActivity}
· onTaskDescriptionChanged
· onTaskMovedToFront: taskId=36
· onRecentTaskListUpdated
· onTaskFocusChanged: taskId=36, focused=true
· onTaskDescriptionChanged
· onTaskDisplayChanged: taskId=36, newDisplayId=3
· onTaskStackChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged

点击 ADB KIT 输入框,输入法正常弹出

1
2
3
4
5
6
7
8
9
10
11
12
10-08 17:18:57.282 W/InputDispatcher(  617): Focused display #0 does not have a focused window.
10-08 17:18:57.282 E/InputDispatcher( 617): But another display has a focused window
10-08 17:18:57.282 E/InputDispatcher( 617): FocusedWindows:
10-08 17:18:57.282 E/InputDispatcher( 617): displayId=8, name='ca56884 com.nightmare.adbkit/com.nightmare.adbkit.MainActivity'
10-08 17:18:57.284 D/SulaInputManager( 2110): action -> 0, pointerId -> 0, position -> Position{point=Point[x=225, y=478], screenSize=1043x782}, pressure -> 1.0, actionButton -> 0, buttons -> 0, source -> 20482, displayId -> 8
10-08 17:18:57.286 D/NeoInputDispatcher( 1989): injectEvent总耗时: 1452708 ns, 总平均耗时: 1126043 ns
10-08 17:18:57.386 D/SulaInputManager( 2110): action -> 1, pointerId -> 0, position -> Position{point=Point[x=225, y=478], screenSize=1043x782}, pressure -> 1.0, actionButton -> 0, buttons -> 0, source -> 20482, displayId -> 8
10-08 17:18:57.386 D/NeoInputDispatcher( 1989): injectEvent总耗时: 417042 ns, 总平均耗时: 1114420 ns
10-08 17:18:57.392 I/ImeTracker( 3464): com.nightmare.adbkit:6c16344f: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
10-08 17:18:57.394 D/InputMethodManager( 3464): showSoftInput() view=p3.t{69cebd4 VFE...... .F....ID 0,0-1043,782 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
10-08 17:18:57.455 D/WindowManagerShell( 868): onKeepClearAreasChanged: restricted={}, unrestricted={Rect(0, 1499 - 1080, 2274)}
10-08 17:18:57.491 D/View ( 1120): requestLayout : getStackTrace(Thread.java:1841) <- printStackStrace(View.java:27969) <- requestLayout(View.java:27999) <- setLayoutParams(View.java:20530) <- updateViewLayout(WindowManagerGlobal.java:464) <- updateViewLayout(WindowManagerImpl.java:165) <- notifyUpdateLayoutParams(TaskbarActivityContext.java:1528) <- onTaskbarOrBubblebarWindowHeightOrInsetsChanged(TaskbarInsetsController.kt:186) <- lambda$onIsStashedChanged$7(TaskbarStashController.java:938) <- $r8$lambda$V70CIL_pG5t21J73JvZUvAfU2B0(null:0) <- run(D8$$SyntheticClass:0) <- runAfterInit(TaskbarControllers.java:263) <- onIsStashedChanged(TaskbarStashController.java:935) <- lambda$createAnimToIsStashed$3(TaskbarStashController.java:620) <- $r8$lambda$lICNXpsaTGWd-5rxHyDFfQwiuw4(null:0) <- run(D8$$SyntheticClass:0) <- onAnimationEnd(AnimatorListeners.java:54) <- onAnimationEnd(Animator.java:708) <- call(D8$$SyntheticClass:0) <- callOnList(Animator.java:666) <-

Android 15(一加 Pad2 Pro)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
· onTaskCreated: taskId=2142, componentName=null
· onTaskDisplayChanged: taskId=2142, newDisplayId=4
· onTaskCreated: taskId=2142, componentName=ComponentInfo{com.nightmare.adbkit/com.nightmare.adbkit.MainActivity}
· onTaskDescriptionChanged
· onTaskMovedToFront: taskId=2142
· onRecentTaskListUpdated
· onTaskFocusChanged: taskId=2142, focused=true
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskStackChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
10-08 17:51:49.880 W/WindowManager( 3201): obj = Display{#0 state=ON size=3392x2400 ROTATION_90}, willRemove = true
10-08 17:51:49.880 W/WindowManager( 3201): java.lang.Throwable
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.wm.WindowList.remove(WindowList.java:51)
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.wm.WindowContainer.positionChildAt(WindowContainer.java:1124)
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.wm.WindowManagerService.moveDisplayToTopInternal(WindowManagerService.java:3684)
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.wm.WindowState.handleTapOutsideFocusInsideSelf(WindowState.java:7060)
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.wm.WindowManagerService.onPointerDownOutsideFocusLocked(WindowManagerService.java:9897)
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.wm.WindowManagerService.-$$Nest$monPointerDownOutsideFocusLocked(Unknown Source:0)
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:6382)
10-08 17:51:49.880 W/WindowManager( 3201): at android.os.Handler.dispatchMessage(Handler.java:112)
10-08 17:51:49.880 W/WindowManager( 3201): at android.os.Looper.loopOnce(Looper.java:288)
10-08 17:51:49.880 W/WindowManager( 3201): at android.os.Looper.loop(Looper.java:393)
10-08 17:51:49.880 W/WindowManager( 3201): at android.os.HandlerThread.run(HandlerThread.java:85)
10-08 17:51:49.880 W/WindowManager( 3201): at com.android.server.ServiceThread.run(ServiceThread.java:46)
10-08 17:51:49.881 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:Window{8e376c6 u0 com.nightmare.neo/com.nightmare.neo.MainActivity} mCurrentFocus:null
10-08 17:51:49.881 V/WindowManager( 3201): Changing focus from null to Window{8e376c6 u0 com.nightmare.neo/com.nightmare.neo.MainActivity},diplayid=0
10-08 17:51:49.882 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:Window{db8692d u0 com.nightmare.adbkit/com.nightmare.adbkit.MainActivity} mCurrentFocus:Window{db8692d u0 com.nightmare.adbkit/com.nightmare.adbkit.MainActivity}
10-08 17:51:49.882 W/InputDispatcher( 3201): Focused display #0 does not have a focused window.
10-08 17:51:49.882 E/InputDispatcher( 3201): But another display has a focused window
10-08 17:51:49.882 E/InputDispatcher( 3201): FocusedWindows:
10-08 17:51:49.882 E/InputDispatcher( 3201): displayId=50, name='db8692d com.nightmare.adbkit/com.nightmare.adbkit.MainActivity'
10-08 17:51:49.889 D/SulaInputManager(23439): action -> 0, pointerId -> 0, position -> Position{point=Point[x=261, y=246], screenSize=1575x1181}, pressure -> 1.0, actionButton -> 0, buttons -> 0, source -> 4098, displayId -> 50
10-08 17:51:49.889 D/ResourcesManagerExtImpl(23356): applyConfigurationToAppResourcesLocked app.getDisplayId() return callback.displayId:0
10-08 17:51:49.890 I/InputDispatcher( 3201): injectInputEvent: targetUid=<not set>, syncMode=NONE, timeout=30000ms, policyFlags=0x8000000, event=MotionEvent { action=DOWN, id[0]=0, x[0]=261, y[0]=246, eventTime=7356385000000, downTime=7356385000000, deviceId=0, source=TOUCHSCREEN, displayId=50, eventId=0xc537f3b1}
10-08 17:51:49.890 D/NeoInputDispatcher(23356): injectEvent总耗时: 4523646 ns, 总平均耗时: 2913528 ns
10-08 17:51:49.890 D/SulaInputManager(23439): action -> 2, pointerId -> 0, position -> Position{point=Point[x=261, y=246], screenSize=1575x1181}, pressure -> 1.0, actionButton -> 0, buttons -> 0, source -> 4098, displayId -> 50
10-08 17:51:49.890 I/InputDispatcher( 3201): injectInputEvent: targetUid=<not set>, syncMode=NONE, timeout=30000ms, policyFlags=0x8000000, event=MotionEvent { action=MOVE, id[0]=0, x[0]=261, y[0]=246, eventTime=7356385000000, downTime=7356385000000, deviceId=0, source=TOUCHSCREEN, displayId=50, eventId=0xe5fa5935}
10-08 17:51:49.890 D/NeoInputDispatcher(23356): injectEvent总耗时: 404271 ns, 总平均耗时: 2411677 ns
10-08 17:51:49.898 I/InputDispatcher( 3201): NFW_setFocusedWindow, 8e376c6 com.nightmare.neo/com.nightmare.neo.MainActivity on display 0, same as the previous:0
10-08 17:51:49.899 I/InputDispatcher( 3201): updateFocusedWindow, 8e376c6 com.nightmare.neo/com.nightmare.neo.MainActivity on display 0, reason: Window became focusable. Previous reason: NOT_FOCUSABLE, result: FocusedWindows:
10-08 17:51:49.899 I/InputDispatcher( 3201): displayId=0, name='8e376c6 com.nightmare.neo/com.nightmare.neo.MainActivity'
10-08 17:51:49.899 I/InputDispatcher( 3201): displayId=50, name='db8692d com.nightmare.adbkit/com.nightmare.adbkit.MainActivity'
10-08 17:51:49.900 D/InputMethodManagerService( 3201): isSecurity: attribute.packageName = com.nightmare.neo mCurMethodId = com.sohu.inputmethod.sogouoem/.SogouIME isSecurity = false ( enable = true needShow = false inBlackList = false isExist = true )
10-08 17:51:49.914 D/SulaInputManager(23439): action -> 1, pointerId -> 0, position -> Position{point=Point[x=261, y=246], screenSize=1575x1181}, pressure -> 1.0, actionButton -> 0, buttons -> 0, source -> 4098, displayId -> 50
10-08 17:51:49.914 I/InputDispatcher( 3201): injectInputEvent: targetUid=<not set>, syncMode=NONE, timeout=30000ms, policyFlags=0x8000000, event=MotionEvent { action=UP, id[0]=0, x[0]=261, y[0]=246, eventTime=7356409000000, downTime=7356385000000, deviceId=0, source=TOUCHSCREEN, displayId=50, eventId=0xef639bda}
10-08 17:51:49.914 D/NeoInputDispatcher(23356): injectEvent总耗时: 715573 ns, 总平均耗时: 2128993 ns
10-08 17:51:49.916 I/ImeTracker(23156): com.nightmare.adbkit:758ea094: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
10-08 17:51:49.916 D/InputMethodManager(23156): showSoftInput() view=p3.t{b5eeff8 VFE...... .F...... 0,0-1575,1181 #1 aid=1073741824 alpha=1.0 viewInfo = } flags=0 reason=SHOW_SOFT_INPUT
10-08 17:51:49.917 E/WindowManager( 3201): isInputMethodClientFocus: display ID mismatch. from client: 50 from window: 0
10-08 17:51:49.917 W/InputMethodManagerService( 3201): Ignoring showSoftInput of uid 10315 : com.android.internal.inputmethod.IInputMethodClient$Stub$Proxy@8c4f9d9

10-08 17:51:49.916 I/ImeTracker(23156): com.nightmare.adbkit:758ea094: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
10-08 17:51:49.916 D/InputMethodManager(23156): showSoftInput() view=p3.t{b5eeff8 VFE…… .F…… 0,0-1575,1181 #1 aid=1073741824 alpha=1.0 viewInfo = } flags=0 reason=SHOW_SOFT_INPUT
10-08 17:51:49.917 E/WindowManager( 3201): : display ID mismatch. from client: 50 from window: 0
10-08 17:51:49.917 W/InputMethodManagerService( 3201): Ignoring showSoftInput of uid 10315 : com.android.internal.inputmethod.IInputMethodClient$Stub$Proxy@8c4f9d9

无界点击 adbkit 输入框

10-08 18:22:24.728 I/ImeTracker( 9277): com.nightmare.adbkit:21d90019: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
10-08 18:22:24.728 D/InputMethodManager( 9277): showSoftInput() view=p3.t{1d8fa3f VFE…… .F…… 0,0-3392,2400 #1 aid=1073741824 alpha=1.0 viewInfo = } flags=0 reason=SHOW_SOFT_INPUT

· add handler -> DisplayManagerPlugin
· add handler -> ActivityManagerPlugin
· add handler -> ActivityTaskManagerPlugin
· add handler -> FilePlugin
· add handler -> DeviceInfoPlugin
· Display 0 (内置屏幕) IME Policy: 0
· add handler -> InputManagerPlugin
· onTaskCreated: taskId=2144, componentName=null
· onTaskDisplayChanged: taskId=2144, newDisplayId=5
· onTaskCreated: taskId=2144, componentName=ComponentInfo{com.nightmare.adbkit/com.nightmare.adbkit.MainActivity}
· onTaskDescriptionChanged
· onTaskMovedToFront: taskId=2144
· onTaskFocusChanged: taskId=2144, focused=true
· onRecentTaskListUpdated
· onRecentTaskListUpdated
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskDescriptionChanged
· onTaskStackChanged
· onTaskDescriptionChanged

{
  "id": 2144,
  "taskId": 2144,
  "persistentId": 2144,
  "displayId": 5,
  "affiliatedTaskId": 0,
  "topActivityType": 1,
  "topActivityInfo": "ActivityInfo{56262f8 com.nightmare.adbkit.MainActivity}",
  "isVisible": true,
  "isRunning": true,
  "isFocused": true,
  "topPackage": "com.nightmare.adbkit",
  "topActivity": "com.nightmare.adbkit.MainActivity",
  "label": "ADB KIT"
},
{
  "id": 2143,
  "taskId": 2143,
  "persistentId": 2143,
  "displayId": 0,
  "affiliatedTaskId": 0,
  "topActivityType": 1,
  "topActivityInfo": "ActivityInfo{8dbe3d1 com.nightmare.neo.MainActivity}",
  "isVisible": true,
  "isRunning": true,
  "isFocused": true,
  "topPackage": "com.nightmare.neo",
  "topActivity": "com.nightmare.neo.MainActivity",
  "label": "TheNeoDesktop"
},

· onTaskFocusChanged: taskId=2150, focused=false
· onTaskFocusChanged: taskId=5, focused=true

焦点没有发生变化

moveTaskToBack 有没可能可以暂停task

继续

在 Neo Activity 中加入以下代码

1
2
3
4
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
);

这个时候,不管是启动 Flutter App 还是其他,通过 adb tap 或者 scrcpy 都无法弹起输入法

所以这个是不是依赖当前 Activity 的焦点?

scrcpy的指针鼠标可以出现在虚拟显示器上

用模拟器试试各个版本的情况

用 Pixso 画一个一模一样的光标

Code LFA 启动速度比较慢

https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java;drc=5dcfeb3f23e95207838e3e15d76336fe69f896c1;l=8309

将 VD 的 Surface 置空,Task 会暂停吗?

com/android/server/inputmethod/InputMethodManagerService.java -> showSoftInputLocked -> canInteractWithImeLocked
-> isImeClientFocused ->
com/android/server/wm/WindowManagerInternal.java -> hasInputMethodClientFocus
com/android/server/wm/WindowManagerService.java -> hasInputMethodClientFocus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@GuardedBy("ImfLock.class")
private boolean canInteractWithImeLocked(int uid, IInputMethodClient client, String methodName,
@Nullable ImeTracker.Token statsToken, @UserIdInt int userId) {
final var userData = getUserData(userId);
if (userData.mCurClient == null || client == null
|| userData.mCurClient.mClient.asBinder() != client.asBinder()) {
// We need to check if this is the current client with
// focus in the window manager, to allow this call to
// be made before input is started in it.
final ClientState cs = mClientController.getClient(client.asBinder());
if (cs == null) {
ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN);
if (!isImeClientFocused(userData.mImeBindingState.mFocusedWindow, cs)) {
Slog.w(TAG, String.format("Ignoring %s of uid %d : %s", methodName, uid, client));
return false;
}
}
ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_CLIENT_FOCUSED);
return true;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Override
public @ImeClientFocusResult int hasInputMethodClientFocus(IBinder windowToken,
int uid, int pid, int displayId) {
if (displayId == Display.INVALID_DISPLAY) {
return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
InputTarget target = getInputTargetFromWindowTokenLocked(windowToken);
if (target == null) {
return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
final int tokenDisplayId = target.getDisplayContent().getDisplayId();
if (tokenDisplayId != displayId) {
Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch."
+ " from client: " + displayId
+ " from window: " + tokenDisplayId);
return ImeClientFocusResult.DISPLAY_ID_MISMATCH;
}
if (displayContent == null
|| displayContent.getDisplayId() != displayId
|| !displayContent.hasAccess(uid)) {
return ImeClientFocusResult.INVALID_DISPLAY_ID;
}

if (target.isInputMethodClientFocus(uid, pid)) {
return ImeClientFocusResult.HAS_IME_FOCUS;
}
// Okay, how about this... what is the current focus?
// It seems in some cases we may not have moved the IM
// target window, such as when it was in a pop-up window,
// so let's also look at the current focus. (An example:
// go to Gmail, start searching so the keyboard goes up,
// press home. Sometimes the IME won't go down.)
// Would be nice to fix this more correctly, but it's
// way at the end of a release, and this should be good enough.
final WindowState currentFocus = displayContent.mCurrentFocus;
if (currentFocus != null && currentFocus.mSession.mUid == uid
&& currentFocus.mSession.mPid == pid) {
return currentFocus.canBeImeTarget() ? ImeClientFocusResult.HAS_IME_FOCUS
: ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
}
return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}

一加启动失败的打印

Ignoring showSoftInput of uid 10315 : com.android.internal.inputmethod.IInputMethodClient$Stub$Proxy@8c4f9d9

单独打开 adbkit 唤起输入法

10-08 18:19:19.613 I/ImeTracker( 5797): com.nightmare.adbkit:7d8691a: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
10-08 18:19:19.614 D/InputMethodManager( 5797): showSoftInput() view=p3.t{585e08c VFE…… .F….ID 0,0-3392,2400 #1 aid=1073741824 alpha=1.0 viewInfo = } flags=0 reason=SHOW_SOFT_INPUT
10-08 18:19:19.614 D/InputMethodManagerService( 3201): isSecurity: attribute.packageName = com.nightmare.adbkit mCurMethodId = com.sohu.inputmethod.sogouoem/.SogouIME isSecurity = false ( enable = true needShow = false inBlackList = false isExist = true )

无界点击 adbkit 输入框

10-08 18:22:24.728 I/ImeTracker( 9277): com.nightmare.adbkit:21d90019: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
10-08 18:22:24.728 D/InputMethodManager( 9277): showSoftInput() view=p3.t{1d8fa3f VFE…… .F…… 0,0-3392,2400 #1 aid=1073741824 alpha=1.0 viewInfo = } flags=0 reason=SHOW_SOFT_INPUT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
10-08 18:21:12.947 D/SurfaceFlinger( 2161): VRR [SurfaceFlinger] setDesiredActiveMode: displayId: 4630946983774026899, renderRate: 120, vsyncRate: 120, event: 0
10-08 18:21:12.955 I/InputDispatcher( 3201): injectInputEvent: targetUid=<not set>, syncMode=NONE, timeout=30000ms, policyFlags=0x8000000, event=MotionEvent { action=DOWN, id[0]=0, x[0]=766, y[0]=470, eventTime=9119450000000, downTime=9119450000000, deviceId=0, source=TOUCHSCREEN, displayId=58, eventId=0xee1e5ae4}
10-08 18:21:13.014 I/InputDispatcher( 3201): injectInputEvent: targetUid=<not set>, syncMode=NONE, timeout=30000ms, policyFlags=0x8000000, event=MotionEvent { action=UP, id[0]=0, x[0]=766, y[0]=470, eventTime=9119509000000, downTime=9119450000000, deviceId=0, source=TOUCHSCREEN, displayId=58, eventId=0xcea6be40}
10-08 18:21:13.025 I/ImeTracker( 9277): com.nightmare.adbkit:52821b3a: onRequestShow at ORIGIN_CLIENT reason SHOW_SOFT_INPUT fromUser false
10-08 18:21:13.027 D/InputMethodManagerService( 3201): isSecurity: attribute.packageName = com.nightmare.adbkit mCurMethodId = com.sohu.inputmethod.sogouoem/.SogouIME isSecurity = false ( enable = true needShow = false inBlackList = false isExist = true )
10-08 18:21:13.027 D/InputMethodManager( 9277): showSoftInput() view=p3.t{1d8fa3f VFE...... .F....ID 0,0-3392,2400 #1 aid=1073741824 alpha=1.0 viewInfo = } flags=0 reason=SHOW_SOFT_INPUT
10-08 18:21:13.030 I/[UAH_CLIENT]( 3201): UahEventAcquire, cmdid:39, pkg:none, identity:InputMethodManagerServiceExtImpl
10-08 18:21:13.055 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.055 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.055 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.055 D/OplusWindowManagerServiceEnhance( 3201): setInsetAnimationTid: pid 8911 ,tid 8911 ,enable false ,uafEnable true
10-08 18:21:13.056 D/WindowManagerServiceExtImpl( 3201): extractConfigInfoAndRealFlags rotation=1, screenDp=0, residentWS:0, scenario:0, bounds:3392, serverWinBounds:Rect(0, 0 - 3392, 2400), win=Window{cf2f837 u0 InputMethod}
10-08 18:21:13.056 V/WindowManager( 3201): Relayout Window{cf2f837 u0 InputMethod}: viewVisibility=0, oldvis=4, req=3392x2316, vsysui=FULLSCREEN LAYOUT_FULLSCREEN LIGHT_NAVIGATION_BAR, x=0, y=0
10-08 18:21:13.056 I/WindowManager( 3201): Window{cf2f837 u0 InputMethod} state from NO_SURFACE to DRAW_PENDING; reason: resetDrawState
10-08 18:21:13.056 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.057 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.057 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.058 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:Window{b83f938 u0 com.nightmare.adbkit/com.nightmare.adbkit.MainActivity} mCurrentFocus:Window{b83f938 u0 com.nightmare.adbkit/com.nightmare.adbkit.MainActivity}
10-08 18:21:13.058 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:null mCurrentFocus:null
10-08 18:21:13.058 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:null mCurrentFocus:null
10-08 18:21:13.058 D/CompactWindowManagerService( 3201): shortComponentName = com.nightmare.uncon/.MainActivity
10-08 18:21:13.060 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.061 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.061 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.066 I/WindowManager( 3201): Window{cf2f837 u0 InputMethod} state from DRAW_PENDING to COMMIT_DRAW_PENDING; reason: finishDrawingLocked
10-08 18:21:13.067 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.068 I/WindowManager( 3201): Window{cf2f837 u0 InputMethod} state from COMMIT_DRAW_PENDING to READY_TO_SHOW; reason: commitFinishDrawingLocked
10-08 18:21:13.068 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.068 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.068 D/WindowManagerServiceExtImpl( 3201): extractConfigInfoAndRealFlags rotation=1, screenDp=914, residentWS:0, scenario:0, bounds:3392, serverWinBounds:Rect(0, 0 - 3392, 2400), win=Window{cf2f837 u0 InputMethod}
10-08 18:21:13.068 V/WindowManager( 3201): Relayout Window{cf2f837 u0 InputMethod}: viewVisibility=0, oldvis=0, req=3392x2316, vsysui=FULLSCREEN LAYOUT_FULLSCREEN LIGHT_NAVIGATION_BAR
10-08 18:21:13.068 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.069 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.069 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.069 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:Window{b83f938 u0 com.nightmare.adbkit/com.nightmare.adbkit.MainActivity} mCurrentFocus:Window{b83f938 u0 com.nightmare.adbkit/com.nightmare.adbkit.MainActivity}
10-08 18:21:13.069 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:null mCurrentFocus:null
10-08 18:21:13.069 D/WindowManager( 3201): NFW_findFocusedWindowIfNeeded:null mCurrentFocus:null
10-08 18:21:13.069 D/CompactWindowManagerService( 3201): shortComponentName = com.nightmare.uncon/.MainActivity
10-08 18:21:13.069 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.070 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.070 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.070 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.070 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.070 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.073 I/OverviewProxyService( 3947): onOplusSystemBarAttributesChanged:appearance 8 packageName com.nightmare.uncon displayId 0 behavior 1
10-08 18:21:13.074 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.074 I/WindowManager( 3201): Window{cf2f837 u0 InputMethod} state from READY_TO_SHOW to HAS_DRAWN; reason: performShowLocked
10-08 18:21:13.074 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.074 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.075 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.075 D/OplusWindowManagerServiceEnhance( 3201): setInsetAnimationTid: pid 9054 ,tid 9054 ,enable true ,uafEnable true
10-08 18:21:13.075 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.075 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.076 D/WindowManagerShell( 3947): onKeepClearAreasChanged: restricted={}, unrestricted={Rect(0, 991 - 1541, 2081)}
10-08 18:21:13.078 D/DisplayModeDirector( 3201): setAppRequest displayId : 58 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.078 D/DisplayModeDirector( 3201): setAppRequest displayId : 0 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.078 D/DisplayModeDirector( 3201): setAppRequest displayId : 57 modeId: 0 requestedRefreshRate: 0.0 requestedMinRefreshRateRange: 0.0 requestedMaxRefreshRateRange: 0.0
10-08 18:21:13.080 D/SurfaceFlinger( 2161): VRR [SurfaceFlinger] setDesiredActiveMode: displayId: 4630946983774026899, renderRate: 120, vsyncRate: 120, event: 0
10-08 18:21:13.225 D/OplusWindowManagerServiceEnhance( 3201): setInsetAnimationTid: pid 9054 ,tid 9054 ,enable false ,uafEnable true
10-08 18:21:13.230 D/SurfaceFlinger( 2161): VRR [SurfaceFlinger] setDesiredActiveMode: displayId: 4630946983774026899, renderRate: 120, vsyncRate: 120, event: 0
10-08 18:21:16.012 D/SurfaceFlinger( 2161): VRR [SurfaceFlinger] setDesiredActiveMode: displayId: 4630946983774026899, renderRate: 60, vsyncRate: 60, event: 0


/android/server/wm/WindowManagerService.java

    @Override
    public void moveDisplayToTopIfAllowed(int displayId) {
        WindowManagerService.this.moveDisplayToTopIfAllowed(displayId);
    }

         @Override
    public int getTopFocusedDisplayId() {
        synchronized (mGlobalLock) {
            return mRoot.getTopFocusedDisplayContent().getDisplayId();
        }
    }

总结

我在Ontap 的时候,切换 Task 焦点,但是用的是 AAS 框架而不是 Shizuku Wrapper

其中的 onTaskStackChanged 误导了我

setFocusedTask no
setFocusedRootTask no

作者

梦魇兽

发布于

2025-10-08

更新于

2025-12-09

许可协议

评论