Android 7 静默安装/卸载实现与 Framework 修改记录
在 RK 平台Android 7项目中因为客户需要支持APK 静默安装/卸载只能从 Framework 层下手做一套完整方案。网上常见做法基本都是直接调用pm命令。实测下来pm install基本没有问题可以绕过“未知来源”提示完成安装pm uninstall在应用内调用时会抛异常最终还是回到系统源代码从 Framework 层分析调用链并做了小改动实现了可用的静默安装 / 卸载能力。先看系统自带的卸载行为从桌面拖动应用到“卸载”区域本质上最终会调用PackageManager.deletePackage来完成卸载frameworks/base/core/java/android/content/pm/PackageManager.java/** * Attempts to delete a package. Since this may take a little while, the * result will be posted back to the given observer. A deletion will fail if * the calling context lacks the * {link android.Manifest.permission#DELETE_PACKAGES} permission, if the * named package cannot be found, or if the named package is a system * package. * * param packageName The name of the package to delete * param observer An observer callback to get notified when the package * deletion is complete. * {link android.content.pm.IPackageDeleteObserver#packageDeleted} * will be called when that happens. observer may be null to * indicate that no callback is desired. * hide */SuppressWarnings(HiddenAbstractMethod)RequiresPermission(Manifest.permission.DELETE_PACKAGES)UnsupportedAppUsagepublicabstractvoiddeletePackage(NonNullStringpackageName,NullableIPackageDeleteObserverobserver,DeleteFlagsintflags);不过deletePackage是hide的隐藏 API虽然可以通过引入framework.jar反射调用但工程侵入性较大、维护成本也高因此还是优先选择基于pm命令的方式来做静默卸载。原理pm命令最终是落到 Framework 层的PackageInstallerService这个类来执行的。相关源码文件位置如下frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.javaOverridepublicvoiduninstall(VersionedPackageversionedPackage,StringcallerPackageName,intflags,IntentSenderstatusReceiver,intuserId){finalComputersnapshotmPm.snapshotComputer();finalintcallingUidBinder.getCallingUid();snapshot.enforceCrossUserPermission(callingUid,userId,true,true,uninstall);// Create by yeruilai 2025-11-01 20:46:06 Enable silent loading for System users// if ((callingUid ! Process.SHELL_UID) (callingUid ! Process.ROOT_UID)) {if((callingUid!Process.SHELL_UID)(callingUid!Process.ROOT_UID)(callingUid!Process.SYSTEM_UID)){mAppOps.checkPackage(callingUid,callerPackageName);}// Check whether the caller is device owner or affiliated profile owner, in which case we do// it silently.DevicePolicyManagerInternaldpmiLocalServices.getService(DevicePolicyManagerInternal.class);finalbooleancanSilentlyInstallPackagedpmi!nulldpmi.canSilentlyInstallPackage(callerPackageName,callingUid);uninstall相关逻辑默认只允许ROOT_UID与SHELL_UID这两个用户执行所以通过ADB shell执行pm uninstall可以正常卸载shell 权限在普通应用里直接执行pm uninstall就会因权限校验失败而抛异常如果业务 APK 本身是系统应用Manifest 中指定android.uid.system那么只需要在对应逻辑里把SYSTEM_UID也加入白名单即可允许系统应用通过pm uninstall执行卸载。如果希望普通应用也能调用pm uninstall那就更“简单粗暴”——把下面这行校验直接注释掉即可mAppOps.checkPackage(callingUid,callerPackageName);但这等于完全放开了调用方校验风险较大仅建议在强可控的定制系统或调试环境里使用不推荐在量产环境这样修改。封装实现静默安装 / 卸载工具类下面是对静默安装 / 卸载做的一层简单封装方便在业务代码中统一调用与回调处理。1. 安装 / 卸载回调接口publicinterfaceApkInstallListener{voidonSuccess(StringpackageName);voidonFail(Exceptione);}2. 静默安装方法基于pm installpublicsynchronizedstaticvoidinstallApk(Contextcontext,StringapkPath,ApkInstallListenerinstallListener){try{PackageInfoinfogetApkInfo(context,apkPath);String[]args{pm,install,-i,info.packageName,--user,0,-r,apkPath};ProcessBuilderprocessBuildernewProcessBuilder(args);Processprocessnull;BufferedReadersuccessResultnull;BufferedReadererrorResultnull;StringBuildersuccessMsgnewStringBuilder();StringBuildererrorMsgnewStringBuilder();try{processprocessBuilder.start();successResultnewBufferedReader(newInputStreamReader(process.getInputStream()));errorResultnewBufferedReader(newInputStreamReader(process.getErrorStream()));Stringline;while((linesuccessResult.readLine())!null){successMsg.append(line);}while((lineerrorResult.readLine())!null){errorMsg.append(line);}Log.i(TAG,install success_msg successMsg);Log.i(TAG,install error_msg errorMsg);if(successMsg.toString().contains(Success)){if(installListener!null){installListener.onSuccess(info.packageName);}}else{if(installListener!null){installListener.onFail(newRuntimeException(errorMsg.toString()));}}}catch(Exceptione){Log.i(TAG,install fail ,e);if(installListener!null){installListener.onFail(e);}}finally{try{if(successResult!null){successResult.close();}}catch(IOExceptionignored){}try{if(errorResult!null){errorResult.close();}}catch(IOExceptionignored){}try{if(process!null){process.destroy();}}catch(Exceptionignored){}}}catch(Exceptione){if(installListener!null){installListener.onFail(e);}}}3. 获取 APK 信息的工具方法/** * 获取 apk 包的信息版本号名称图标等 * * param absPath apk 包的绝对路径 * param context Context */privatestaticPackageInfogetApkInfo(Contextcontext,StringabsPath){PackageInfopkgInfonull;try{PackageManagerpmcontext.getPackageManager();pkgInfopm.getPackageArchiveInfo(absPath,PackageManager.GET_ACTIVITIES);if(pkgInfo!null){ApplicationInfoappInfopkgInfo.applicationInfo;// 必须加这两句否则 icon 获取不到 apk 自身的图标appInfo.sourceDirabsPath;appInfo.publicSourceDirabsPath;StringappNamepm.getApplicationLabel(appInfo).toString();StringpackageNameappInfo.packageName;StringversionpkgInfo.versionName;StringpkgInfoStrString.format(PackageName:%s, Version:%s, AppName:%s,packageName,version,appName);Log.i(YTUtils,String.format(PkgInfo: %s,pkgInfoStr));}}catch(Exceptione){Log.e(YTUtils,apk 文件信息读取失败: absPath,e);}returnpkgInfo;}4. 静默卸载方法基于pm uninstallpublicsynchronizedstaticvoiduninstallApk(Contextcontext,StringpackageName,ApkInstallListenerinstallListener){try{String[]args{pm,uninstall,-k,--user,0,packageName};ProcessBuilderprocessBuildernewProcessBuilder(args);Processprocessnull;BufferedReadersuccessResultnull;BufferedReadererrorResultnull;StringBuildersuccessMsgnewStringBuilder();StringBuildererrorMsgnewStringBuilder();try{processprocessBuilder.start();successResultnewBufferedReader(newInputStreamReader(process.getInputStream()));errorResultnewBufferedReader(newInputStreamReader(process.getErrorStream()));Stringline;while((linesuccessResult.readLine())!null){successMsg.append(line);}while((lineerrorResult.readLine())!null){errorMsg.append(line);}Log.i(TAG,uninstall success_msg successMsg);Log.i(TAG,uninstall error_msg errorMsg);if(successMsg.toString().contains(Success)){if(installListener!null){installListener.onSuccess(packageName);}}else{if(installListener!null){installListener.onFail(newRuntimeException(errorMsg.toString()));}}}catch(Exceptione){Log.i(TAG,uninstall fail ,e);if(installListener!null){installListener.onFail(e);}}finally{try{if(successResult!null){successResult.close();}}catch(IOExceptionignored){}try{if(errorResult!null){errorResult.close();}}catch(IOExceptionignored){}try{if(process!null){process.destroy();}}catch(Exceptionignored){}}}catch(Exceptione){if(installListener!null){installListener.onFail(e);}}}以上就是在Android 7上通过少量 Framework 修改配合pm命令实现静默安装 / 卸载的整体思路与代码封装。

相关新闻

不用PLC,视觉直接控制电机,跳过控制器,直接发动作指令。

不用PLC,视觉直接控制电机,跳过控制器,直接发动作指令。

纯视觉直驱电机控制系统一、实际应用场景描述在工业自动化生产线中,产品分拣是最常见的应用场景之一。传统方案通常需要:传送带光电传感器PLC控制器伺服驱动器电机,形成复杂的控制链路。以瓶装饮料分拣线为例:- 传送带运送不同颜色…

2026/7/4 12:58:25 阅读更多 →
书籍-鲍里斯·格列科夫+亚历山大·雅库博夫斯基《金帐汗国兴衰史》

书籍-鲍里斯·格列科夫+亚历山大·雅库博夫斯基《金帐汗国兴衰史》

鲍里斯格列科夫,亚历山大雅库博夫斯基《金帐汗国兴衰史》详细介绍 书籍基本信息 书名:金帐汗国兴衰史 作者:[苏]鲍里斯格列科夫(Boris Grekov),亚历山大雅库博夫斯基(Alexander Yakubovsky&…

2026/7/5 3:44:43 阅读更多 →
圣母大学与里海大学:全球首个LLM时代学术引用真实性检验工具

圣母大学与里海大学:全球首个LLM时代学术引用真实性检验工具

在人工智能迅猛发展的今天,大型语言模型正在深刻改变着学术写作的方式。然而,一个严峻的问题悄然浮现:这些看似万能的AI助手有时会"凭空捏造"学术引用,创造出看起来合理但实际上并不存在的论文引用。这项由圣母大学和里…

2026/7/7 0:18:31 阅读更多 →

最新新闻

AI写教材必备:5款高效工具推荐,轻松生成教材还能低查重!

AI写教材必备:5款高效工具推荐,轻松生成教材还能低查重!

在教材编写的过程中,如何平衡原创性和合规性是一个必须重视的关键问题。很多创作者会借鉴一些优秀教材的内容,却又担心自己的作品面临过高的查重率;如果试图自主创作知识点表述,可能又会导致逻辑不够严谨或者内容有误。引用他人研…

2026/7/8 13:04:32 阅读更多 →
OpenCV 4.x 图像处理 3 步实战:300x300 尺寸转换与 L1 距离验证

OpenCV 4.x 图像处理 3 步实战:300x300 尺寸转换与 L1 距离验证

OpenCV 4.x 图像处理实战:从基础操作到L1距离验证计算机视觉作为人工智能领域的重要分支,正在深刻改变我们与数字世界的交互方式。OpenCV作为最流行的开源计算机视觉库,为开发者提供了强大的工具集。本文将带您从零开始掌握OpenCV的核心图像处…

2026/7/8 13:04:32 阅读更多 →
mpvue:用 Vue.js 开发小程序的框架

mpvue:用 Vue.js 开发小程序的框架

文章目录mpvue:用 Vue.js 开发小程序的框架mpvue:用 Vue.js 开发小程序的框架 美团点评开源了一款名为 mpvue 的前端框架,它让开发者用 Vue.js 的语法来开发小程序。这个项目在 GitHub 上收获了超过 2 万颗星。 mpvue 基于 Vue.js 2.4.1 版本…

2026/7/8 13:02:26 阅读更多 →
让Foobar2000的歌词体验超越原版:ESLyric-LyricsSource深度解读

让Foobar2000的歌词体验超越原版:ESLyric-LyricsSource深度解读

让Foobar2000的歌词体验超越原版:ESLyric-LyricsSource深度解读 【免费下载链接】ESLyric-LyricsSource Advanced lyrics source for ESLyric in foobar2000 项目地址: https://gitcode.com/gh_mirrors/es/ESLyric-LyricsSource 还记得那个让你在Foobar2000播…

2026/7/8 13:02:26 阅读更多 →
专业干货!低查重的AI教材编写工具,助你高效产出教材!

专业干货!低查重的AI教材编写工具,助你高效产出教材!

编写教材难题催生AI需求 在编写教材的过程中,总是会碰到进度缓慢的各种难题。虽然框架和相关资料都准备齐全,但在写具体内容时却卡壳了——一句话琢磨半天,最终仍觉得表达不够准确;章节之间的衔接,想了半天也找不到合…

2026/7/8 13:02:26 阅读更多 →
从绿的谐波×SKF合资到美光93亿扩产:大规模量产时代的设备选型逻辑

从绿的谐波×SKF合资到美光93亿扩产:大规模量产时代的设备选型逻辑

2026年7月7日,制造业释放了三个信号:绿的谐波与SKF成立合资公司聚焦高精密轴承规模化供应、通用技术沈阳机床第1000条组合产线交付、美光93亿美元扩建广岛HBM工厂、日月光宣布调涨先进封装报价。三个信号指向同一个趋势:大规模量产时代的设备…

2026/7/8 12:58:23 阅读更多 →

日新闻

3大核心能力重塑《明日方舟》游戏体验:MAA自动化助手的革命性突破

3大核心能力重塑《明日方舟》游戏体验:MAA自动化助手的革命性突破

3大核心能力重塑《明日方舟》游戏体验:MAA自动化助手的革命性突破 【免费下载链接】MaaAssistantArknights 《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients. 项目地址: …

2026/7/8 0:00:48 阅读更多 →
MyBatis 批量操作深度优化——从 N+1 到批处理的全路径

MyBatis 批量操作深度优化——从 N+1 到批处理的全路径

MyBatis 批量操作深度优化——从 N1 到批处理的全路径 一、从"功能正确"到"性能可接受"——MyBatis 批量操作的三段式进化 MyBatis 在日常增删改查场景中几乎是无感的——实体映射直观、SQL 控制灵活。但当数据量从千级上升到十万级、百万级,许…

2026/7/8 0:00:48 阅读更多 →
工业负载控制方案:TPD2015FN与PIC18F45K22应用解析

工业负载控制方案:TPD2015FN与PIC18F45K22应用解析

1. 工业负载控制方案概述在工业自动化、电机驱动和照明控制等高需求场景中,可靠地控制电感和电阻负载是核心挑战之一。TPD2015FN作为东芝的8通道高端智能功率开关IC,配合PIC18F45K22微控制器,能够构建一套稳定、高效的负载控制系统。这套组合…

2026/7/8 0:02:48 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/7 14:24:45 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/7 12:34:47 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/7 15:59:06 阅读更多 →

月新闻