Android 16系统App开机自启动方案

关键根因分析:

  1. 触发了 Android 的 BAL 拦截机制Background activity launch blocked。在后台广播接收器(callingUidProcState: RECEIVER)中直接通过 startActivity 启动界面,被 Android 10+(在 Android 14/15/16 中执行得极为严苛)的安全策略硬性拦截
  2. 你的 App 目前并不是真正的系统进程
    • 虽然你在 Manifest 里配置了 sharedUserId="android.uid.system"persistent="true",但日志中显示 callingUid: 10186 (这是典型的第三方普通应用 UID u0a186),而真正获得系统权限的应用 UID 必须是 1000
    • isCallingUidPersistentSystemProcess: false 也直接证明:你的 App 没有成功以系统 uid 运行 (原因通常是:APK 没有使用系统平台签名密钥签名,导致系统忽略了 sharedUserId 并作为普通应用安装)。

解决方案

方案 A:设为主屏幕/Launcher(最推荐、无需任何权限豁免)

Android 系统的设计原则是开机必定启动一个"桌面/Launcher"。将你的 App 声明为 Launcher,系统开机会自动直接展示它,完全不走后台拉起逻辑,不受 BAL 限制。

修改 AndroidManifest.xml 找到 com.micbotics.robot.activity.MainActivity,在其 <intent-filter> 中加入 HOMEDEFAULT

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)签名

  1. 获取系统签名密钥 : 从系统固件源码编译端获取 platform.pk8platform.x509.pem

  2. 重签名 APK : 使用 Google 提供的 signapk.jar 或在 Android Studio 配置系统签名打包:

    bash 复制代码
    java -jar signapk.jar platform.x509.pem platform.pk8 old.apk new-signed.apk
  3. 卸载重装 : 必须先卸载原来的 App(adb uninstall com.juwei.move),然后再安装带有系统签名的 APK。 此时再看日志,你的 callingUid 会变成 1000isCallingUidPersistentSystemProcess 会变成 true,系统将直接允许你从后台广播启动 Activity。


方案 C:如果只是普通应用开发(无系统签名),通过悬浮窗权限绕过 BAL

日志中表明该应用是普通应用(UID 10186),Android 允许具有"悬浮窗(Display over other apps)"权限的应用在后台启动 Activity。

  1. 手动赋予悬浮窗权限(或通过 adb 命令):

    bash 复制代码
    adb shell appops set com.juwei.move SYSTEM_ALERT_WINDOW allow
  2. BootReceiver.java 中配置 ActivityOptions (Android 14/15/16 必备): 在 Android 14+ 中,即使有悬浮窗权限,后台调用 startActivity 也必须显式 opt-in 允许后台启动:

    java 复制代码
    public 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)。
相关推荐
hm宋2 小时前
安卓手机卡顿优化 —— 背景与使用指南
android
OxYGC2 小时前
[Android] [WatchOS]智能手表第一篇: 实用工具与应用推荐
android·智能手表
嘟哩DuliDuli4 小时前
创作 Agent 的任务状态该如何保存
android·java·javascript
Godikov5 小时前
两个 SDK 抢一个摄像头:Android 设备上的相机仲裁设计与踩坑实录
android
Godikov5 小时前
摄像头电机自动校准实战:为什么我用「步数计数」替换了角度传感器绝对定位
android
Godikov5 小时前
2GB 内存的 Android 工业终端上,我们是怎么把 OOM 按在地上摩擦的
android
Godikov5 小时前
暗光下摄像头"见鬼"了:一个 2GB 内存 Android 终端的帧差唤醒踩坑记
android