Android 系统应用重名install安装失败分析解决

Android 系统应用重名install安装失败分析解决

文章目录

  • [Android 系统应用重名install安装失败分析解决](#Android 系统应用重名install安装失败分析解决)
    • 一、前言
      • [1、Android Persistent apps 简单介绍](#1、Android Persistent apps 简单介绍)
    • [二、系统 persistent 应用直接安装需求分析解决](#二、系统 persistent 应用直接安装需求分析解决)
    • 三、其他
      • 1、persistent系统应用install安装调试小结
      • 2、安装persistent系统应用报错在系统源码中简要分析
      • [3、Android Studio 直接安装persistent系统应用报错分析处理](#3、Android Studio 直接安装persistent系统应用报错分析处理)
        • [(1) Android Studio 中弹框显示的错误](#(1) Android Studio 中弹框显示的错误)
      • [(2)app\build.gradle 适配](#(2)app\build.gradle 适配)
        • [(3)修改 applicationId](#(3)修改 applicationId)
        • [(4)dumpsys package 验证安装的apk](#(4)dumpsys package 验证安装的apk)
      • [4、Android14 Bluetooth系统蓝牙应用调试](#4、Android14 Bluetooth系统蓝牙应用调试)
        • (1)查看蓝牙应用的安装目录
        • [(2)apex目录apk 无法直接替换!](#(2)apex目录apk 无法直接替换!)
        • [(4)蓝牙应用可以直接install 安装成功](#(4)蓝牙应用可以直接install 安装成功)
        • [(5)dumpsys 查看蓝牙应用安装替换情况](#(5)dumpsys 查看蓝牙应用安装替换情况)
      • [5、系统应用设置 persistent="true" 的作用](#5、系统应用设置 persistent="true" 的作用)
      • 5、判断应用是否是persistent类型的系统应用
        • [(1)AndroidManifest.xml 代码判断签名和属性](#(1)AndroidManifest.xml 代码判断签名和属性)
        • (2)应用apk文件反编译分析
        • [(3)运行环境dumpsys package分析](#(3)运行环境dumpsys package分析)
      • [6、普通应用设置 persistent="true" 有用吗?](#6、普通应用设置 persistent="true" 有用吗?)

一、前言

系统开发过程中,你会发现一些系统应用编译后无法直接安装成功,为啥?

具体是为啥导致无法正常安装?如果要正常安装需要怎么处理?

刚开始我以为是系统应用重名不能直接安装,但是发现有些系统应用是可以直接安装的;

所以还是要研究看看。

本文简单分析解决一下这个问题!

后面复现关键就是 Android 的 persistent 属性,

persistent(翻译:持久的) 属性是系统应用用来保活的应用和服务的。

声明了 android:persistent="true"的系统应用就表示该应用是持久的,无法进行安装。

这种持久系统应用情况,除了替换apk重启或者编译系统大包,如何才能直接替换安装apk调试呢?

下面进行分析一下。这个对系统应用的调试有一定的帮助作用。

1、Android Persistent apps 简单介绍

Persistent apps(持久化应用)是指那些被标记为具有android:persistent="true"属性的应用;

这个属性是定义的AndroidManifest.xml 的 application 标签中;

这种应用一般是系统应用,该进程在系统运行过程中会被特殊对待,kill掉后马上会被系统拉起;

目前系统一些Persistent apps 应用:

复制代码
系统核心服务应用:SystemUI,输入法框架
关键通信和同步应用:电话应用,短信应用,同步服务应用
还有一些自定义的关键应用

简单的定义 persistent 属性的代码:

复制代码
    <application
        android:name=".base.MyApplication"
   ...
        android:persistent="true" //持久化
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/SettingsDialog"
        tools:replace="android:appComponentFactory">

如果未定义persistent属性,就是默认的false。普通应用都是未定义这个属性的。

如果普通应用定义这个属性为true,会发生什么呢?最后结论有揭晓。

二、系统 persistent 应用直接安装需求分析解决

并不是Android的所有系统应用都是无法install 安装,

有部分应用是可以直接install安装的,

无法直接install安装的应用,除了 persistent 这种情况,

可能还有其他情况,比如版本兼容,版本号定义等情况,下面只是针对persistent这种情况进行分析处理。

1、系统应用安装报错返回的信息

复制代码
C:\Users\As11040>adb install E:\Android14\311D2\apk\DebugSystemUI\DebugSystemUI.apk
Performing Streamed Install
adb: failed to install E:\Android14\311D2\apk\DebugSystemUI\DebugSystemUI.apk: 
Failure [INSTALL_FAILED_INVALID_APK: Package com.debug.SystemUI is a persistent app. Persistent apps are not updateable.]

C:\Users\As11040>

这里可以看到报错提示是:Persistent apps 无法更新。

那咋搞?下面就提供一些调试persistent应用的思路。

2、分析解决

下面给出三种解决方案:

复制代码
第一种方法(每次重启):
    系统remount后,替换DebugSystemUI后,重启就会自动安装替换;
第二种方法(不用重启):
    Android Studio 修改DebugSystemUI的包名,编译这个 应用,修改包名后,就可以安装调试;
第三种方法(重启一次):
    去除系统应用代码的persistent属性,重新编译,按照第一种方法替换apk后,
    后面不用修改应用的包名,就可以直接安装替换apk调试。

上面看起来好像没有一种是非常简单的。实际开发中,调试系统应用可能还会更麻烦。

下面对比一下几种调试手段:

复制代码
第一种调试方法:
    比较保守,大部分系统开发的都是这样处理的;
    这种调试方法适用于次数较少,简单代码修改的调试;
    每次都要重启比较麻烦耗时。

第二种调试方法:
    Android Studio 中重命名应用包名 applicationId "com.debug.settings2",
    不用修改其他包名相关的地方,就可以直接安装了;
    这种调试方法适用于次数较多或者复杂的代码修改的调试;
    但是这种方法不一定适用于所有应用,自定义开发的应用比较好适配,可以直接用Studio编译;
    有些系统应用不一定能导入到Android Studio中进行编译,或者要适配过程是非常麻烦的;
    系统应用还有导入系统签名,系统framework包和其他相关包;
    如果是非常麻烦的情况就没必要搞了,有些情况不一定能搞得定。

第三种调试方法:
    这种方法比较中和;
    如果既要多次修改,又无法使用Android Studio 进行编译代码的情况;
    第三种调试方法没有用到Android Studio,源码编译就行。

具体情况,使用具体方式就行调试吧。怎么方便怎么搞就行。

上面其实就是说了一下persistent系统应用无法直接安装,

具体要怎么调试就看当时的调试条件了。

三、其他

1、persistent系统应用install安装调试小结

系统代码中限制了persistent的系统应用无法进行直接安装调试;

大概是因为persistent是一直运行的,无法stop或者kill;

如果要调试安装persistent系统应用,大概有三种方法:

复制代码
(1)替换系统应用apk,重启
(2)把apk源码弄到Android Studio中,修改包名后,直接安装
(3)去系统应用的persistent属性,替换重启后,后面安装调试就不用每次重启了

第二种方法看起来比较简单,虽然不用remount设备,但是有些大型应用适配是非常麻烦的;

第一种和第三种方法都是要解锁设备,remount后才能执行的;

第三种方法是比较简单方便多次调试的。

2、安装persistent系统应用报错在系统源码中简要分析

上面是解决了调试问题,但是不妨分析一下系统源代码,加深一下印象。

(1)logcat 过滤查看安装apk相关日志:
复制代码
//这里过来安装或者应用管理相关日志信息
console:/ # logcat | grep -i -E "install|PackageManager"

12-10 18:25:08.591 32730 32730 I abb     : StartCommandInProcess(7061636b61676500696e7374616c6c00 package.install. [truncated])
12-10 18:25:08.794   891   955 E PackageManager: No required verifiers
12-10 18:25:08.815   891   955 I PackageManager: Integrity check passed for file:///data/app/vmdl1963375571.tmp
//源码代码中报错的信息:
12-10 18:25:08.828   891   955 W PackageManager: Package com.skg.SystemUI is a persistent app. Persistent apps are not updateable.
//返回到cmd窗口中打印的信息:
12-10 18:25:08.831   891   955 D PackageInstallerSession: Marking session 1963375571 as failed: 
INSTALL_FAILED_INVALID_APK: Package com.skg.SystemUI is a persistent app. Persistent apps are not updateable.

从上面大致可以看到报错的信息,

然后就可以从源码中看看具体发生错误的具体代码位置。

查找过程就不展开说明了,可以源码中grep找到关键字筛选可能的位置。

(2)安装失败报错的具体代码位置

Android14 源码:

framework/base/services/core/java/com/android/server/pm/InstallPackageHelper.java

复制代码
final class InstallPackageHelper {
    private final PackageManagerService mPm;
    
    ...
    
    @GuardedBy("mPm.mInstallLock")
    private void preparePackageLI(InstallRequest request) throws PrepareFailure {
        final int installFlags = request.getInstallFlags();
        final boolean onExternal = request.getVolumeUuid() != null;
        ...

                  // Prevent persistent apps from being updated
                    if (oldPackage.isPersistent()
                            && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
                        throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
                                "Package " + oldPackage.getPackageName() + " is a persistent app. "
                                        + "Persistent apps are not updateable.");
                    }
    ...

从上面Java代码确实看到了logcat中的报错信息。

上面逻辑看到是安装应用的时候判断了isPersistent()和 installFlags 后就抛出这个异常错误。

isPersistent() 方法很容易理解就是是否定义了 persistent属性的应用;

installFlags 属性应该是一些列属性相关的值,比如是否系统应用,系统签名都是相关的,

这里不继续进行分析,有兴趣的可以看看。

3、Android Studio 直接安装persistent系统应用报错分析处理

这个相当于接上面第二种调试方法展开说明一下。

(1) Android Studio 中弹框显示的错误
复制代码
Error running 'app' The application could not be installed:
INSTALL_FAILED_INVALID_APK The APKs are invalid. List of apks: [0]
'E:\Studio\project\Android14_311D2\DebugSettings\app\build\intermediates\apk\debug\app-debug.apk

从上面查看并未发现为啥报错,只显示了apk不可用!

哈哈,这种情况咋分析?

其实还得看logcat日志,同样的过滤install|PackageManager是可以看到persistent应用无法替换的错误提示。

这里介绍一下,修改Android Studio 系统应用包名的处理。

(2)app\build.gradle 适配

原本的包名定义:

复制代码
android {
    namespace 'com.debug.settings'
    compileSdk 34

    defaultConfig {
        applicationId "com.debug.settings"
        minSdk 30
        targetSdk 34
        versionCode 1
        versionName "1.0"

    }

    //系统签名、证书信息在这里配置
   
}

...

上面可以看到有个namespace 和 applicationId 都是定义的应用包名。

其中 namespace 是跟系统资源绑定的,不能随便改,

如果要修改namespace,Java代码里面的定义都要修改,否则会报错;

applicationId 是一个封装的包名,可以随便修改!

(3)修改 applicationId

如果要重新安装这个系统应用的封装包名,修改applicationId 的包名就行:

复制代码
applicationId "com.debug.settings2"

安装调试发现,会重新生成另外一个 com.debug.settings2 包名的应用,之前的应用是不影响。

(4)dumpsys package 验证安装的apk

同时dumpsys package 查看也是验证查看这个新的包名:

复制代码
IWB:/ $ dumpsys package com.debug.settings | grep path
    path: /system/priv-app/DebugSettings/DebugSettings.apk
IWB:/ $
IWB:/ $ dumpsys package com.debug.settings2 | grep path
    path: /data/app/~~w3MWD86p2cPawWc2MJun7A==/com.debug.settings2-S3W73_reyyTmvIiB-Vaggg==/base.apk

IWB:/ $ dumpsys package com.debug.settings2 | grep version
    versionCode=1 minSdk=30 targetSdk=34
    versionName=3.0
IWB:/ $

从上面日志看,第一次dumps 这个应用的包名和路径是 system 目录的;

安装系统应用后,重新dumpsys 这个应用的包名和路径是data 目录下的;

说明这个系统应用是安装上去的。还可以查看version 或者time 确认。

另外要注意的是:

复制代码
在Android Studio编译的apk version版本是在app\build.gradle 里面定义使能的,
AndroidManifest.xml定义是没用的,会被覆盖!

4、Android14 Bluetooth系统蓝牙应用调试

蓝牙应用可能调试的人不多,我之前是偶尔要调试的;

但是发现蓝牙apk应用无法替换,因为Android14 开始有些应用是apex目录下的,无法remount这个目录!

所以一般都是编译整个大包进行调试的;

但是通过上面的知识了解后,发现蓝牙apk是可以直接install的,

以前白走了冤枉路,所以有更多知识储备是很有必要的,虽然有时候不一定马上用得上。

下面就是分析系统蓝牙应用的安装替换过程:

(1)查看蓝牙应用的安装目录
复制代码
console:/ # dumpsys package com.android.bluetooth | grep path
    path: /apex/com.android.btservices/app/Bluetooth@UP1A.231105.001.B2/Bluetooth.apk

蓝牙apk 居然在apex目录!

这个是从Android14 开发才会这样的,有些模块系统放在了apex目录下;

Android13 或者更低的版本都是放在system或者system_ext目录下的。是可以解锁后替换apk的。

调试发现 apex 目录默认是不开放的,无法解锁替换!

(2)apex目录apk 无法直接替换!
复制代码
C:\Users\As11040>adb root
restarting adbd as root

C:\Users\As11040>adb remount
Verity is already disabled
Remounted /system as RW
Remounted /system_ext as RW
Remounted /system_dlkm as RW
Remounted /vendor as RW
Remounted /product as RW
Remounted /odm as RW
Remounted /vendor_dlkm as RW
Remounted /odm_dlkm as RW
Remounted /oem as RW
Remount succeeded

C:\Users\As11040>adb shell
IWB:/ # cd /apex/com.android.btservices/app
IWB:/apex/com.android.btservices/app # mkdir aa
mkdir: 'aa': Read-only file system
1|IWB:/apex/com.android.btservices/app #
1|IWB:/apex/com.android.btservices/app # cd ../../..
IWB:/ # cd system/priv-app/
IWB:/system/priv-app # mkdir aa
IWB:/system/priv-app #

上面执行命令代码可以看到即使root和remount,也是无法获取到apex目录的修改权限。

adb push 是试过的,也是报错提示:Read-only file system

没有其他办法的情况,只能编译大包进行调试了。

但是通过上面的persistent知识,获取到了灵感,

感觉可以试试直接install看看是否报错,即使报错也是可以解决的。

####(3)查看蓝牙应用是否是 persistent 应用

系统蓝牙apk应用声明persistent情况:

release\packages\modules\Bluetooth\android\app\AndroidManifest.xml

复制代码
   <!-- For PBAP Owner Vcard Info -->
    <uses-permission android:name="android.permission.READ_PROFILE"/>
    <application android:name="com.android.bluetooth.btservice.AdapterApp"
         android:icon="@mipmap/bt_share"
         android:persistent="false"
         android:label="@string/app_name"
         android:supportsRtl="true"
         android:usesCleartextTraffic="false"
         android:directBootAware="true"
         android:defaultToDeviceProtectedStorage="true"
         android:memtagMode="async">

这里查看蓝牙应用设置persistent为false,那么应该是可以直接安装的。

如果persistent为true,可以设置为false编译一次,后面就可以一直调试。

尝试直接安装是成功的:

(4)蓝牙应用可以直接install 安装成功
复制代码
C:\Users\As11040>adb install Bluetooth.apk
Performing Streamed Install
Success

成功了,哈哈。

(5)dumpsys 查看蓝牙应用安装替换情况
复制代码
console:/ # dumpsys package com.android.bluetooth | grep path
    path: /apex/com.android.btservices/app/Bluetooth@UP1A.231105.001.B2/Bluetooth.apk

console:/ # dumpsys package com.android.bluetooth | grep path
    path: /data/app/~~zptgNMJxOrNyEASJFt3HKg==/com.android.bluetooth-KHInQ7LVcEAHQhjYOSEcJw==/base.apk
console:/ # 

从上面日志看,蓝牙应用确实是从系统应用,变成一个应该普通的install的应用。

这里蓝牙应用 persistent="false" 是可以直接install 安装的关键。

这里讲解蓝牙只是一个参考,Android14 后面估计还会有很多系统应用会放到apex目录下,

比如nfc等,所以多学掉调试手段是有用的。

5、系统应用设置 persistent="true" 的作用

确实起到了保活作用,

am force-stop 不会有反应,pid没变;应该是上层拦截了!但是看不到任何相关日志!

kill pid,后应用马上就又被拉起来了。

下面是一些命令调试验证日志:

复制代码
130|console:/ # 
//(1)查看应用进程情况,包含进程id,父进程,启动时间,应用包名等信息
130|console:/ # ps -eff | grep com.skg.settings
system        6317  5441 0 11:01:28 ?     00:00:02 com.skg.settings

//(2)强制停止应用
console:/ # am force-stop com.skg.settings
console:/ # 
//(3))强制停止后,查看进程信息是没有变化的
console:/ # ps -eff | grep com.skg.settings                                    
system        6317  5441 0 11:01:29 ?     00:00:02 com.skg.settings
console:/ # 
//(4)杀死进程id试试
console:/ # kill 6317
console:/ # 
//(5)杀死进程id后,进程是有重新启动的,会生成新的进程id
console:/ # ps -eff | grep com.skg.settings                       
system       20485  5441 10 11:28:12 ?    00:00:00 com.skg.settings
console:/ # 
//(6)杀死父进程id试试
console:/ # kill 5441
console:/ # 
//(7)杀死进程id后,父进程和进程都会重新生成,系统会黑屏一下,估计是系统上层重启了,
//所以最好不要杀父进程,有可能会导致系统重启,或者无法正常开机,一般断开上电可以恢复
console:/ # ps -eff | grep com.skg.settings
system       24353 23431 41 11:34:08 ?    00:00:01 com.skg.settings
console:/ # 

从上面日志看,

am force-stop 是无法停止应用的;

直接kill进程是可以杀死应用,但是马上就会重启启动新的进程;

所以

如果应用有继承Application,可以看到应用重开拉起来的时候有执行onCreate,可以重新初始化自己需要的东西。

上面说了那么多好像没说怎么从非代码手段判断一个应用是是否是 persistent的系统应用。

其实只要有apk或者运行环境,也是可以判断的。

5、判断应用是否是persistent类型的系统应用

下面从三个维度判断这个应用是否是persistent类型的系统应用,

源码下、应用apk文件、运行环境。

(1)AndroidManifest.xml 代码判断签名和属性
复制代码
1、查看根目录是否声明了系统签名权限:android:sharedUserId="android.uid.system"
2、查看Application 是否声明了:persistent="true"
(2)应用apk文件反编译分析

apk咋判断?其实就是反编译,查看 AndroidManifest.xml 文件,这个和第一个方式类似。

大部分系统文件都是没有经过复杂混淆的,看AndroidManifest里面的基本信息是没啥问题的。

反编译工具命令就不展示了,其实Android Studio 就可以简单反编译,

把apk拖到一个Studio的项目,就会反编译看到AndroidManifest的内容:

简单示例如下:

uid信息:

persistent信息:

上面就相当于看到了源码的整个 AndroidManifest 的信息。

声明的权限、Acitivity和Service那些四大组件信息都是有的,想看啥就看啥。

(3)运行环境dumpsys package分析

dumpsys package 可以看到应用的很多具体信息,

比如签名情况,安装时间,安装路径,版本号,应用获取到的权限等信息,persistent 也是可以看到的。

下面是判断判断过滤appid和persistent的示例日志:

复制代码
//1、查看系统uid签名的应用appid都是1000;flags里面查看是否有 PERSISTENT 属性
console:/ # 
console:/ # dumpsys package com.dubug.settings | grep -i -E "appid|persistent"
    appId=1000
    flags=[ SYSTEM DEBUGGABLE HAS_CODE PERSISTENT ALLOW_CLEAR_USER_DATA UPDATED_SYSTEM_APP TEST_ONLY ALLOW_BACKUP ]
    pkgFlags=[ SYSTEM DEBUGGABLE HAS_CODE PERSISTENT ALLOW_CLEAR_USER_DATA UPDATED_SYSTEM_APP TEST_ONLY ALLOW_BACKUP ]
    appId=1000
    appId=1000
console:/ # 

//2、查看蓝牙应用,可以看到是不是uid签名,其实是bluetooth签名,也没有PERSISTENT属性
console:/ # dumpsys package com.android.bluetooth | grep -i -E "appid|persistent"
    appId=1002
    appId=1002
    appId=1002
console:/ # 
//3、查看一个普通应用,appId id不是1000,有没有PERSISTENT已经不重要了!
console:/ # dumpsys package com.demo.listdemo | grep -i -E "appid|persistent"
    appId=10070
console:/ # 
console:/ # 
//后面尝试在普通应用加入persistent="true"声明,在apk的flags里面也是未发现有PERSISTENT属性标签,
//说明PERSISTENT属性生效是有一定条件的,比如需要系统签名权限。

6、普通应用设置 persistent="true" 有用吗?

其实没啥用,我试过了。可以直接安装,,不会安装失败,也不会保活。

这个应该就是 InstallPackageHelper.java里面判断了是否系统应用,否则这个persistent属性没啥意义。

本文只是想说系统应用声明persistent="true"会导致无法直接安装,没想到附带了这么多小知识。

相关推荐
雨白11 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk11 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING12 小时前
RN容器启动优化实践
android·react native
恋猫de小郭14 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker19 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴19 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭1 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农2 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos