关键根因分析:
- 触发了 Android 的 BAL 拦截机制 :
Background activity launch blocked。在后台广播接收器(callingUidProcState: RECEIVER)中直接通过startActivity启动界面,被 Android 10+(在 Android 14/15/16 中执行得极为严苛)的安全策略硬性拦截。 - 你的 App 目前并不是真正的系统进程 :
- 虽然你在 Manifest 里配置了
sharedUserId="android.uid.system"和persistent="true",但日志中显示callingUid: 10186(这是典型的第三方普通应用 UIDu0a186),而真正获得系统权限的应用 UID 必须是1000。 isCallingUidPersistentSystemProcess: false也直接证明:你的 App 没有成功以系统 uid 运行 (原因通常是:APK 没有使用系统平台签名密钥签名,导致系统忽略了sharedUserId并作为普通应用安装)。
- 虽然你在 Manifest 里配置了
解决方案
方案 A:设为主屏幕/Launcher(最推荐、无需任何权限豁免)
Android 系统的设计原则是开机必定启动一个"桌面/Launcher"。将你的 App 声明为 Launcher,系统开机会自动直接展示它,完全不走后台拉起逻辑,不受 BAL 限制。
修改 AndroidManifest.xml: 找到 com.micbotics.robot.activity.MainActivity,在其 <intent-filter> 中加入 HOME 和 DEFAULT:
xml
<activity
android:name=".activity.MainActivity"
android:exported="true"
android:directBootAware="true"
android:enabled="true"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- 新增这两行 -->
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
如果设备上有原生的 Launcher(如 Launcher3),开机可能会弹出一次"选择默认桌面",选择你的 App 并勾选"始终"即可;若为定制固件,直接把原生 Launcher 删掉或者设置为默认即可。
方案 B:修复系统权限(让 UID 变回 1000)
你的 Manifest 已经写好了大量系统特权,但之所以被当成普通应用(UID 10186),是因为没有用这台 Android 16 车机/机器人主板的原厂平台签名(platform key)签名。
-
获取系统签名密钥 : 从系统固件源码编译端获取
platform.pk8和platform.x509.pem。 -
重签名 APK : 使用 Google 提供的
signapk.jar或在 Android Studio 配置系统签名打包:bashjava -jar signapk.jar platform.x509.pem platform.pk8 old.apk new-signed.apk -
卸载重装 : 必须先卸载原来的 App(
adb uninstall com.juwei.move),然后再安装带有系统签名的 APK。 此时再看日志,你的callingUid会变成1000,isCallingUidPersistentSystemProcess会变成true,系统将直接允许你从后台广播启动 Activity。
方案 C:如果只是普通应用开发(无系统签名),通过悬浮窗权限绕过 BAL
日志中表明该应用是普通应用(UID 10186),Android 允许具有"悬浮窗(Display over other apps)"权限的应用在后台启动 Activity。
-
手动赋予悬浮窗权限(或通过 adb 命令):
bashadb shell appops set com.juwei.move SYSTEM_ALERT_WINDOW allow -
在
BootReceiver.java中配置ActivityOptions(Android 14/15/16 必备): 在 Android 14+ 中,即使有悬浮窗权限,后台调用startActivity也必须显式 opt-in 允许后台启动:javapublic class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { // 建议只拦截 BOOT_COMPLETED,避免两次启动 Intent launchIntent = new Intent(context, MainActivity.class); launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); Bundle optionsBundle = null; if (Build.VERSION.SDK_INT >= 34) { // Android 14 及以上 (含 Android 16) ActivityOptions options = ActivityOptions.makeBasic(); // 设置后台启动模式允许 options.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED ); optionsBundle = options.toBundle(); } try { context.startActivity(launchIntent, optionsBundle); } catch (Exception e) { Log.e("BootReceiver", "Start Activity failed", e); } } } }
总结排查建议:
- 立竿见影的办法 :采用 方案 A ,加两行
category.HOME,系统开机直接进你 App。 - 根治权限的办法 :检查为什么 UID 会是
10186而不是系统级的1000(补上平台签名,走 方案 B)。