Android 系统应用重名install安装失败分析解决
Android 系统应用重名install安装失败分析解决
文章目录
- Android 系统应用重名install安装失败分析解决
一、前言
系统开发过程中,你会发现一些系统应用编译后无法直接安装成功,为啥?
具体是为啥导致无法正常安装?如果要正常安装需要怎么处理?
刚开始我以为是系统应用重名不能直接安装,但是发现有些系统应用是可以直接安装的;
所以还是要研究看看。
本文简单分析解决一下这个问题!
后面复现关键就是 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"会导致无法直接安装,没想到附带了这么多小知识。
原文地址:https://blog.csdn.net/wenzhi20102321/article/details/144408307
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!