[Device] 鸿蒙/EMUI 上 app_prepare 返回 "APS refused native resolution (status=-3)" 时任务直接终止(表现为虚拟屏黑屏),建议降级为告警

Open Beginner friendly
#34 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Domain
backend

Research direction

Read assets/overlays/android_app_launch.py, especially app_start() and its app_prepare handling. Reproduce the reported status=-3 response on the described HarmonyOS device, then trace how launch proceeds when native resolution preparation is refused. Done means this refusal no longer immediately terminates the task, while the existing restart-loop protection is preserved.

Written by the indexing model from the issue text.

Description

环境
项目 环境
设备 HUAWEI DBY-W09(MatePad 11),HarmonyOS 4.2.0.213(C00E100),ro.build.version.release=12,API 31,arm64-v8a
Root Magisk 27.0(后端选的是 ROOT)
AzurPilot for Android 1.3.96、1.3.131 均可复现
游戏 com.bilibili.azurlane
现象
  1. 点「开始运行」后任务立刻失败,应用「虚拟屏」页一直是黑屏,看起来像“模拟器跑不起来”。

  2. session.log 里只有:

    CRITICAL  [错误] 任务需要人工介入(将尝试自动恢复)
              原因:当前状态无法由自动化流程安全判断或修复。
    

    以及更早一次的 设备初始化需要人工介入,都没有真正的异常信息。

  3. 真正的错误只出现在 files/debug/root_launch_debug.log 和直接调桥的返回里:

    java.lang.IllegalStateException: APS refused native resolution for com.bilibili.azurlane (status=-3)
    
复现步骤(概率 100%)
adb forward tcp:22301 tcp:22301          # 桥在本机 127.0.0.1:22301
# 1) 能力探测
{"method":"ping","id":1}
# → {"ok":true,"capabilities":{"nativeGameResolution":true,"appFocus":true,...},"displayId":3,...}

2) 准备原生分辨率(游戏在前台/后台、是否已获得焦点都一样)

{"method":"app_prepare","package":"com.bilibili.azurlane","displayId":3,"id":2}

→ {"ok":false,"error":"APS refused native resolution for com.bilibili.azurlane (status=-3)","id":2}

根因

assets/overlays/android_app_launch.py 里,app_start() 把 app_prepare 当成了致命步骤:

ping = self._azurpilot_android_call({'method': 'ping'})
if ping.get('capabilities', {}).get('nativeGameResolution'):
try:
response = self._azurpilot_android_call({
'method': 'app_prepare', 'package': target, 'displayId': ping['displayId'],
})
if not response.get('ok'):
raise RuntimeError(response.get('error', 'unknown'))
except Exception as error:
# 启动失败不能继续走 Restart,否则同一裁切画面会形成无限重启循环。
raise module.RequestHumanTakeover(
f'无法关闭游戏分辨率缩放,请检查系统权限:{error}') from error

但 app_prepare 的作用只是“关闭兼容缩放”。在鸿蒙上这一步被系统拒绝(status=-3,APS 应该是华为那边的分辨率/缩放服务),
跳过它之后游戏依然能在虚拟屏上以 1280×720 正常渲染,模板匹配、OCR、点击全部正常。

验证:把 app_prepare 的失败降级为告警后,同一台设备同一版本完全跑通

在 _azurpilot_android_call() 里拦截 app_prepare 的失败响应,返回 {'ok': True, 'skipped': True, ...} 并打一条 warning,
其余逻辑不动,日志立刻变成:

00:38:22 应用启动: com.bilibili.azurlane
00:38:22 WARNING AzurPilotAndroid: 跳过原生分辨率准备(APS refused native resolution for com.bilibili.azurlane (status=-3))
00:38:24 [重启] 等待 30.0 秒让应用启动和稳定...
00:38:57 [重启] 应用启动成功并正在运行
00:40:14 [委托] 日常资源开发III
00:40:18 [委托-完成] 委托完成时间: ['2026-10-26 11:00:18', ...]
00:40:18 调度器: 开始任务 Research

虚拟屏截图(screencap -d <vid>)1280×720,战斗画面、演习计数 OCR 均正常。

补充:黑屏的另一半原因(说明 app_focus 是必要的)

游戏通过 am start --display N 拉到虚拟屏后,该显示上 mCurrentFocus=null / mFocusedApp=null,
SurfaceFlinger 里 SurfaceView... 层 isVisible=false、buffer=0x0,screencap -d N 全黑。
对虚拟屏注入一次触摸后:

mCurrentFocus=Window{... com.bilibili.azurlane/com.manjuu.azurlane.MainActivity}
SurfaceView... isVisible=true buffer=slot=62 ...

画面立刻出来。也就是说 app_focus 这一步是对的、必须的,只是它排在 app_prepare 之后,被异常挡掉了。

建议
  1. app_prepare 失败(尤其是 APS refused ... status=-3 这种“系统不支持”类错误)时降级为 logger.warning 并继续,
    和 app_focus 的处理方式保持一致;如果担心裁切问题,可以只在 status=-3 / refused 时降级。
  2. 或者调整顺序:先 app_focus 再 app_prepare,或无论 prepare 成功与否都执行 focus。
  3. alas.py 里 device 属性捕获 RequestHumanTakeover 时调用的是 logger.error_context(..., 不带 exc),
    建议把异常原文带上(exc=e),否则用户只能看到“设备连接或设备参数校验未通过”,无法定位。
临时规避(其他用户可参考)

更新后将 module/device/method/azurpilot_android.py 末尾追加:

def _azp_huawei_call(self, payload, frame=None):
response = _azp_huawei_original(self, payload, frame)
if not response.get('ok') and payload.get('method') == 'app_prepare':
logger.warning('AzurPilotAndroid: 跳过原生分辨率准备(%s)' % response.get('error'))
return {'ok': True, 'skipped': True, 'reason': response.get('error')}
return response

_azp_huawei_original = AzurPilotAndroid._azurpilot_android_call
AzurPilotAndroid._azurpilot_android_call = _azp_huawei_call

注意:应用更新会重新解压运行时,这个补丁需要重打。

Dominant language
Kotlin
Stars
41
Forks
2
Avg merge
12h 33m
Merged PRs (30d)
3

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from wess09/AzurPilot-for-Android

All issues in wess09/AzurPilot-for-Android

Similar issues

More Kotlin issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.