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"会导致无法直接安装,没想到附带了这么多小知识。

相关推荐
白水景东1 小时前
Android 使用Overlay现实主题切换
android
Gin_Zou4 小时前
通过Idea实现与gitee连接并拉取项目和上传
gitee·intellij-idea·源代码管理
JhonKI6 小时前
【MySQL】内置函数详解
android·数据库·mysql
P1sc3s0076 小时前
nexus5x安卓root
android
我叫czc10 小时前
【恶意软件检测论文】通过提取 API 语义来实现的一个新颖的安卓恶意软件检测方法
android
我想睡到自然醒₍˄·͈༝·͈˄*₎◞ ̑11 小时前
【Android】EventBus——进行良好的组件通信
android·java·microsoft·android-studio
凉忆-11 小时前
uniapp打包apk允许横屏竖屏内容翻转
android·服务器·uni-app
喜欢打篮球的普通人12 小时前
Android系统(android app和系统架构)
android
casual_clover12 小时前
Android之RecyclerView显示数据列表和网格
android·kotlin