背景

我有一台基于 Amlogic 平台的电视设备。它原本运行较老的厂商 Android TV 系统,后来刷入了 Android 14 GSI。系统能启动、应用也基本可用,但出现了几个很典型的问题:

  • 系统中明明存在 Google Cast 相关组件,手机却搜索不到电视;
  • 视频应用里偶尔能看到投屏入口,但必须打开对应电视应用才能使用;
  • 系统中能看到名为 Miracast 的原生进程,却无法从手机或 Windows 连接;
  • 应用市场安装 APK 曾经失败,容易让人误以为是 SELinux 或权限导致;
  • 系统版本是 Android 14,但底层 Vendor、内核和硬件能力仍来自较老的厂商系统。

这类问题不能只看“有没有安装某个包”或“有没有一个进程”。投屏是否真正可用,至少要确认三件事:

  1. 接收端服务是否正常启动;
  2. 是否在局域网广播自己的存在;
  3. 对应协议的监听端口和系统路由是否真实工作。

本文记录一套可以复用的排查和解决流程。

先理解四种常见投屏协议

Android TV 上常见的“投屏”其实不是同一种技术。

协议 常见发送端 主要用途 是否要求同一局域网
AirPlay iPhone、iPad、Mac 屏幕镜像、视频、音乐
Google Cast Android、Chrome、Google Home 标签页、桌面、媒体投送
DLNA/DMR 视频应用、VLC、NAS 应用 把媒体地址交给电视播放
Miracast Android 系统无线投屏、Windows Win+K Wi-Fi Direct 屏幕镜像 通常不依赖路由器

其中 DLNA 最容易被误判。优酷、哔哩哔哩等电视应用可能各自启动一个 DLNA 接收进程,并占用 UDP 1900。这只能说明该应用带有投屏功能,不能证明系统拥有一个全局、常驻的投屏接收器。

第一步:连接 ADB 并确认系统信息

先在电视中打开网络 ADB,然后从电脑连接。下文使用脱敏后的示例地址:

adb connect <TV_IP>:5555
adb devices -l

例如:

adb connect 192.168.1.50:5555

确认 Android 版本和 CPU ABI:

adb -s <TV_IP>:5555 shell getprop ro.build.version.release
adb -s <TV_IP>:5555 shell getprop ro.build.version.sdk
adb -s <TV_IP>:5555 shell getprop ro.product.cpu.abilist

这里最容易踩坑的是 CPU 架构。某些 GSI 名称中带有 a64,但用户空间实际只能运行 32 位 ARM 应用。应以 ro.product.cpu.abilist 为准。

如果输出类似:

armeabi-v7a,armeabi

那么应该下载 armeabi-v7auniversal APK,不能下载只有 arm64-v8a 的版本。

第二步:确认现有投屏服务是否真的可用

1. 检查相关包和进程

adb -s <TV_IP>:5555 shell \
  'pm list packages | grep -i -E "cast|mediashell|miracast|wfd|airplay|dlna|upnp"'

adb -s <TV_IP>:5555 shell \
  'ps -A -o PID,PPID,UID,NAME,ARGS | grep -i -E "cast|miracast|projection|dlna"'

发现包或进程只能作为线索,不能直接得出“投屏可用”的结论。

2. 检查系统 MediaRouter

adb -s <TV_IP>:5555 shell dumpsys media_router

如果看到类似状态:

ProviderServiceProxy - package: com.google.android.apps.mediashell
bound: false
connection (active:false, ready:false)
<provider info not received, yet>

说明 Google Cast 包虽然存在,系统 MediaRouter 并没有成功连接它。此时手机通常无法发现电视。

3. 检查监听端口及其所属进程

adb -s <TV_IP>:5555 shell \
  'ss -lntup | grep -E "1900|5353|7000|7236|8008|8009"'

常见端口如下,实际版本可能有所不同:

  • UDP 5353:mDNS,常用于 AirPlay、Google Cast 服务发现;
  • UDP 1900:SSDP,常用于 DLNA/UPnP;
  • TCP 7000:常见 AirPlay 服务端口;
  • TCP 8008/8009:常见 Google Cast 服务端口;
  • TCP 7236:常见 Miracast RTSP 控制端口。

重点不是端口本身,而是 users:(...) 中显示的所属进程。例如 UDP 1900 如果属于某个视频应用,就更可能是应用内投屏,而不是系统级服务。

我的实际判断结果

排查后得到以下结论:

Google Cast

系统中存在 com.google.android.apps.mediashell,相关服务和进程也能启动,但 MediaRouter 显示 Provider 未绑定,最初也没有监听 8008/8009,局域网中没有 _googlecast._tcp 广播。

结论:组件存在,但不能作为可用的全局 Google Cast 接收器。

DLNA

系统中有多个进程监听 UDP 1900。继续按 PID 查询后,发现它们分别属于视频应用自己的投屏进程。

结论:存在应用内 DLNA,但不是稳定的系统级全局接收器。

AirPlay

没有发现现成的 AirPlay 接收服务或 _airplay._tcp 广播。

结论:系统原本不具备 AirPlay 接收能力。

Miracast

Vendor 中有 miracast_hdcp2 进程和库,但它只是 Miracast 的 HDCP 辅助组件。系统没有可用的 Wi-Fi Display 接收入口,也没有活动的 Wi-Fi Direct/P2P 接收状态。

需要特别强调:

  • android.hardware.wifi.direct 表示设备声明支持 Wi-Fi Direct,不等于支持 Miracast Sink;
  • 存在 miracast_hdcp2 进程,也不等于完整 Miracast 接收器已经启动;
  • 真正的 Miracast 接收还依赖 Wi-Fi P2P 接口、WFD Sink、厂商 HAL、显示合成和解码链路。

解决方案:安装一个统一的电视端接收器

为了避免 AirPlay、Cast、DLNA 分别安装多个常驻应用,我选择了 AirScreen。它是面向 Android TV、电视盒子和投影设备的接收端应用,包名为:

com.ionitech.airscreen

它宣称支持 AirPlay、Google Cast、Miracast 和 DLNA。但要注意:应用能够实现前三种局域网投送协议,不代表它一定能绕过底层 Vendor 对 Miracast 的限制。

APK 应该怎样下载

首选官方来源:

如果电视无法访问 Google,可以先在电脑下载,再通过 ADB 安装。使用第三方镜像时应承担额外风险,并至少完成下面的检查:

  1. 包名必须是 com.ionitech.airscreen
  2. 优先选择普通 .apk,避免来源不明的修改版;
  3. CPU 架构选择 armeabi-v7auniversal
  4. 不要给 32 位设备下载仅支持 arm64-v8ax86x86_64 的版本;
  5. 记录文件 SHA-256;
  6. 使用 apksigner 检查 APK 签名。

我在本次排查中使用的下载页面是:

https://apkcombo.com/airscreen/com.ionitech.airscreen/download/apk

这个页面及其文件 CDN 都不是 AirScreen 官方下载源,只适合在无法使用 Google Play/Amazon 时作为备选。本次测试文件的信息如下:

版本:2.16.1
Version Code:574
格式:普通 APK
ABI:armeabi-v7a
SHA-256:0ba3be9d194cdaed78efa3919b2d967af112fd9a3824fabc3db26226d6ff8134

SHA-256 只能用来确认下载到的文件与本文测试文件是否一致,不能单独证明第三方文件就是官方版本。

示例命令:

shasum -a 256 ~/Downloads/AirScreen.apk

apksigner verify --verbose --print-certs \
  ~/Downloads/AirScreen.apk

unzip -l ~/Downloads/AirScreen.apk | \
  grep -E 'lib/(armeabi-v7a|arm64-v8a|x86|x86_64)/'

我的测试环境中,该版本目标 SDK 为 34,并且 APK 中包含 armeabi-v7a 原生库。版本会持续更新,不应长期把 2.16.1 当成唯一正确版本;以后下载时仍应优先选择最新版稳定版,并重新确认 ABI。

如果第三方下载的版本启动后提示“可能是非官方版本”或“请从 Google Play 安装”,不要通过伪造安装来源来绕过。能访问官方商店时,应改用 Google Play/Amazon 版本。

通过 ADB 安装

adb -s <TV_IP>:5555 install -r -g \
  ~/Downloads/AirScreen.apk

参数含义:

  • -r:保留数据并覆盖安装;
  • -g:安装时授予可授予的运行时权限。

安装完成后检查版本和 ABI:

adb -s <TV_IP>:5555 shell \
  'dumpsys package com.ionitech.airscreen | grep -E "versionName=|versionCode=|primaryCpuAbi="'

确认它具有 Android TV 入口:

adb -s <TV_IP>:5555 shell \
  'cmd package resolve-activity --brief \
  -c android.intent.category.LEANBACK_LAUNCHER \
  com.ionitech.airscreen'

启动应用

可以直接从电视应用列表打开,也可以使用 ADB:

adb -s <TV_IP>:5555 shell \
  monkey -p com.ionitech.airscreen \
  -c android.intent.category.LEANBACK_LAUNCHER 1

首次启动后,在设置中确认需要的协议已经开启,并给设备设置一个容易辨认且不包含个人信息的名称,例如:

AS-客厅电视

不要把真实姓名、电话号码、公司名称或精确房间用途放进局域网广播名称中,因为同一网络中的其他设备都可能看到它。

如何验证安装后的服务

从电视检查端口

adb -s <TV_IP>:5555 shell \
  'ss -lntup | grep -E "1900|5353|7000|8008|8009"'

成功启动后,我观察到 AirScreen 持有以下监听:

  • UDP 5353
  • UDP 1900
  • TCP 7000
  • TCP 8008/8009

从 macOS 检查 AirPlay 和 Cast 广播

dns-sd -B _airplay._tcp local.
dns-sd -B _googlecast._tcp local.

看到对应实例名称后按 Ctrl+C 退出。

正常情况下会出现类似名称:

AS-客厅电视[AirPlay]
AS-客厅电视[Cast]

DLNA/DMR 一般显示为:

AS-客厅电视[DMR]

查询 Cast 接收器状态

部分版本可通过下面的地址读取接收器信息:

curl "http://<TV_IP>:8008/setup/eureka_info?options=detail"

返回内容中如果包含 AirScreen、接收器名称和在线状态,说明 Cast 服务已经工作。

手机和电脑应该选择哪个设备

发送设备 操作方式 应选择的接收器
Android 视频应用 点击“TV”或“投屏” AS-客厅电视[DMR],也可尝试 [Cast]
Android 全屏投送 Google Home 或系统支持的 Cast 功能 AS-客厅电视[Cast]
Windows Chrome Chrome 菜单 → 投放,选择标签页或桌面 AS-客厅电视[Cast]
Windows VLC 播放 → 渲染器 AS-客厅电视[DMR]
iPhone/iPad 控制中心 → 屏幕镜像 AS-客厅电视[AirPlay]
Mac 控制中心 → 屏幕镜像 AS-客厅电视[AirPlay]

Android、iPhone、Windows 和 Mac 使用 AirPlay、Cast、DLNA 时,应与电视连接到同一个局域网,并关闭无线路由器的 AP 隔离或访客网络隔离。

为什么 Miracast 仍可能无法使用

安装 AirScreen 后,AirPlay、Cast 和 DLNA 都能正常广播,但 Miracast 没有建立 P2P 接口,也没有监听常见的 7236 端口。Windows Win+K 和 Android 系统无线投屏可能仍然看不到电视。

这通常不是 AirScreen 设置错误,而是 GSI 与旧 Vendor 组合缺少完整的 Miracast Sink 链路。可选方案有:

  1. Android 设备改用 Google Cast;
  2. 视频应用改用 DLNA;
  3. iPhone 和 Mac 使用 AirPlay;
  4. 使用外置 Miracast 接收器;
  5. 刷回与主板、DDR、Wi-Fi 芯片、遥控器和分区布局完全匹配的原厂 TV 固件。

不要仅凭同为某款 SoC 就刷入“通用固件”。电视盒子的 DTB、DDR、Wi-Fi/蓝牙芯片和分区布局不同,刷错可能导致不开机、无网络、无遥控器,甚至需要拆机短接救砖。

关于后台常驻和开机启动

AirScreen 只有在进程或后台服务存活时,手机和电脑才能发现它。可以先在应用内部开启开机启动,再根据系统情况加入 Doze 白名单:

adb -s <TV_IP>:5555 shell \
  cmd deviceidle whitelist +com.ionitech.airscreen

adb -s <TV_IP>:5555 shell \
  am set-inactive com.ionitech.airscreen false

adb -s <TV_IP>:5555 shell \
  cmd appops set com.ionitech.airscreen RUN_IN_BACKGROUND allow

注意:Doze 白名单只是在系统休眠时减少限制,不等于一定能让应用开机自启。厂商系统可能还有单独的自启动、后台保护或省电策略。

不建议同时常驻多个 DLNA/AirPlay 接收器。多个应用同时监听 SSDP/mDNS,可能造成设备列表重复、端口争用、后台占用增加,甚至出现投屏连接到错误应用的情况。

APK 安装失败时不要先关闭 SELinux

遇到 APK 安装失败,应先读取 ADB 的准确错误:

adb -s <TV_IP>:5555 install -r ~/Downloads/example.apk

常见错误包括:

错误 常见原因 处理方式
INSTALL_FAILED_NO_MATCHING_ABIS APK 架构不匹配 换成 armeabi-v7a 或设备支持的 ABI
INSTALL_FAILED_OLDER_SDK APK 要求更高 Android 版本 使用兼容版本
INSTALL_FAILED_UPDATE_INCOMPATIBLE 已安装版本签名不同 备份数据后卸载旧包,再安装正确签名版本
INSTALL_FAILED_VERSION_DOWNGRADE 新装版本号更低 使用更新版本,或明确确认后使用降级参数
INSTALL_FAILED_INSUFFICIENT_STORAGE 数据分区空间不足 清理应用和缓存

检查 SELinux 状态:

adb -s <TV_IP>:5555 shell getenforce

但不要把 setenforce 0 当成通用解决方案。关闭或放宽 SELinux 会降低系统安全性,而且不能修复 ABI 不匹配、签名冲突、MediaRouter Provider 未绑定或缺少 Miracast HAL 等问题。安装普通用户 APK 本身也不需要 Root。

回滚方式

如果 AirScreen 不适合当前设备,可以卸载:

adb -s <TV_IP>:5555 uninstall com.ionitech.airscreen

卸载后重新检查端口,确认没有残留进程:

adb -s <TV_IP>:5555 shell \
  'ps -A | grep com.ionitech.airscreen; \
   ss -lntup | grep -E "1900|5353|7000|8008|8009"'

总结

这次排查最重要的经验不是“安装某个 APK 就能解决一切”,而是要分清组件存在、进程运行、协议广播和真正可连接之间的区别。

  • Google Cast 包存在,不代表系统 MediaRouter 已成功连接它;
  • 视频应用监听 UDP 1900,不代表系统拥有全局 DLNA;
  • Wi-Fi Direct Feature 和 HDCP 辅助进程,不代表 Miracast Sink 完整;
  • AirPlay、Cast、DLNA 可以通过电视端接收应用补齐;
  • Miracast 更依赖 Vendor、HAL 和 Wi-Fi 驱动,GSI 无法保证补齐;
  • APK 安装失败应先看 ABI、签名、版本和空间,不要先关闭 SELinux。

最终,在这套 GSI 系统上,AirPlay、Google Cast 和 DLNA 已经可以被局域网设备发现并使用;Miracast 则需要原厂固件或外置接收器才能获得更可靠的支持。

标签: none

添加新评论