本文主要介绍程序如何利用root权限静默安装(卸载)APK,如何自动选择普通安装(卸载)还是静默安装(卸载)。
1、root权限静默安装(卸载)调用
引入TrineaAndroidCommon@Github(欢迎star和fork^_^)作为你项目的library(如何拉取代码及添加公共库),或自己抽取PackageUtils.installSlient(PackageUtils.uninstallSilent)函数进行调用,系统授权管理会弹出对话框让用户选择是否允许应用获得root权限。允许的话即可静默安装。
该函数返回PackageUtils.INSTALL_SUCCEEDED表示安装成功,失败则返回相应错误码,可以得到失败的详细原因,包括文件不存在,apk无效,系统内存不足,签名不正确,缺少公共库,share user错误等等判断。
注意对于较大apk安装过程非常耗时,所以最好新启线程去调用PackageUtils.installSlient。
2、root权限静默安装实现
PackageUtils.installSlient的实现实际使用的是su pm install -r filePath命令。核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
public static final String COMMAND_SU = "su"; public static final String COMMAND_SH = "sh"; public static final String COMMAND_EXIT = "exit\n"; public static final String COMMAND_LINE_END = "\n"; public static CommandResult execCommand(String[] commands, boolean isRoot, boolean isNeedResultMsg) { int result = -1; if (commands == null || commands.length == 0) { return new CommandResult(result, null, null); } Process process = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; DataOutputStream os = null; try { process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH); os = new DataOutputStream(process.getOutputStream()); for (String command : commands) { if (command == null) { continue; } // donnot use os.writeBytes(commmand), avoid chinese charset error os.write(command.getBytes()); os.writeBytes(COMMAND_LINE_END); os.flush(); } os.writeBytes(COMMAND_EXIT); os.flush(); result = process.waitFor(); // get command result if (isNeedResultMsg) { successMsg = new StringBuilder(); errorMsg = new StringBuilder(); successResult = new BufferedReader(new InputStreamReader(process.getInputStream())); errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream())); String s; while ((s = successResult.readLine()) != null) { successMsg.append(s); } while ((s = errorResult.readLine()) != null) { errorMsg.append(s); } } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (os != null) { os.close(); } if (successResult != null) { successResult.close(); } if (errorResult != null) { errorResult.close(); } } catch (IOException e) { e.printStackTrace(); } if (process != null) { process.destroy(); } } return new CommandResult(result, successMsg == null ? null : successMsg.toString(), errorMsg == null ? null : errorMsg.toString()); } |
其中commands为pm install -r . 从中可以看出主要就是使用su切换到root环境下,再调用pm install -r进行安装。
3、普通安装,系统权限静默安装,root权限静默安装的自动选择
查看PackageUtils源码会发现我还提供了其他几个安装函数,其中PackageUtils.install(PackageUtils.uninstall)函数会根据是否是系统应用以及是否拥有root权限,从而确定调用哪种安装方式(普通安装方式、root静默安装方式还是系统权限静默安装),源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * install according conditions * <ul> * <li>if system application or rooted, see {@link #installSilent(Context, String)}</li> * <li>else see {@link #installNormal(Context, String)}</li> * </ul> * * @param context * @param filePath * @return */ public static final int install(Context context, String filePath) { if (!PackageUtils.isSystemApplication(context) && !ShellUtils.checkRootPermission()) { return installNormal(context, filePath) ? INSTALL_SUCCEEDED : INSTALL_FAILED_INVALID_URI; } return installSilent(context, filePath); } |
如果是系统应用记得添加<uses-permission android:name=”android.permission.INSTALL_PACKAGES” />权限,从而走普通安装方式,不用申请root权限进行静默安装。
4、PackageUtils 实现静默卸载应用
你好.我在cmd 用adb shell 一些命令 比如reboot重启也是会弹出这些信息的 5.0系统
WARNING: linker: could not load library “libsigchain.so” from LD_PRELOAD for “reboot”; caused by “libsigchain.so” is 32-bit instead of 64-bit CANNOT LINK EXECUTABLE: could not load library “libc.so” needed by “reboot”; caused by “libc.so” is 32-bit instead of 64-bit…
请问这个怎么解决
这个没遇到过,可以 Google 看看,是系统问题还是机型问题
好的 我再找找
手机没有root权限可以实现静默安装吗
不行
好的。
你好,我在使用静默安装时(我的设备是有root权限的),会有一定概率出现返回INSTALL_FAILED_OTHER;请问下是由什么原因导致的呢? 静默安装时并没有抛出异常,但是也没有成功或者失败的信息。 result = process.waitFor(); 这个result是一直返回0,说明shell执行是完成了,但是就是没有返回信息。 导致静默安装失效。
打印返回结果中的 errorMsg 看看,另外你自己可以在终端用 pm install -r 安装看看为什么失败
errorMsg返回的是空值
那就自己用 pm install -r 安装试试
LD_LIBRARY_PATH=/vendor/lib:/system/lib pm install -r /storage/emulated/0/mobileSenseTemp/temp/com.tencent.qqmusic.apk 在shell下执行,一直都是成功的…
手动在shell下面执行是没有失败的情况。。但是在使用api时, 成功的情况是有success返回, 失效的情况,process.getInputStream() 和 process.getErrorStream() 没有数据返回,导致静默安装失败。(result = process.waitFor(); 返回 0)
这个有可能是这边的 Bug,按理说 result = process.waitFor(); 返回 0 就表示已经安装成功,只是这个命令比较特殊不是按照 result 判断返回结果而是 successMsg。你检查下这种失败情况是不是实际已经安装成功了?
实际上是没有安装成功的,我加了app安装监听,不管用哪种方式安装,安装成功了我的监听广播都能接收的,现在失效的情况下是没有收到广播的; 所以说root下pm install安装失败了,导致失败时没有返回值信息的bug。 等会再去看下pm install的源码实现。 PS: 我现在是利用系统辅助服务来实现了非root下的apk自动安装,也算能搞定自动安装这个功能模块。(也就是模拟点击安装卸载,适配率95%以上) 最终还是希望能解决这个bug。
哈哈,非 root 你用了 Accessibility 实现的吧,这个适配工作量还是挺大的。root 下的这个 Bug 我暂时没有时间去调试和修复。你可以看看 pm install 的实现,有部分代码也是当初我从里面拷贝出来的。可以看看是随机失败,还是说对于某些固定的 apk 会出现,其他机器是否有类似情况。
对的,适配工作还好吧。好的,感谢~
解决了欢迎告诉我啊
if (!PackageUtils.isSystemApplication(context) && !ShellUtils.checkRootPermission()) { return installNormal(context, filePath) ? INSTALL_SUCCEEDED : INSTALL_FAILED_INVALID_URI; } return installSilent(context, filePath);系统权限也不能静默安装的,也就是说system/app的app也不能做静默安装,必须有root权限才可以,所以这部分代码有问题
“如果是系统应用记得添加权限,从而走普通安装方式,不用申请root权限进行静默安装。” 仔细看这句
“如果是系统应用记得添加权限,从而走普通安装方式,不用申请root权限进行静默安装。” 按楼主的意思,系统应用添加了权限就可以静默安装?不需要系统签名?楼主没试过系统应用的情况吧
应该是你没试过
我就是因为试过了才对你的说法存在疑问,我试了就算是系统应用,没有用系统签名是不能静默安装,不知道文章里面的静默安装到底是怎么实现的?
没有系统签名的系统应用指的是什么应用?
没用platform.pk8和platform.x509.pem签名的应用啊,否则不能做到系统应用静默安装吧
那怎么算得上是系统应用..
不是吧,我理解的系统应用就是预装应用,例如Facebook ,twitter那些,他们是没有用系统签名签名的的。除非是自己做rom又预装自己写的App,这个App才可能做静默安装
预装的不算,必须跟系统一起编译的才能算作上面的系统应用方式静默安装
预装应用也分内置和不内置吧,我的理解是系统应用就是在system/app下出厂的应用。当然你获取root权限后也可以自己push一个进去
你好博主,我调用了你的silentInstall,报错了, Segmentation fault LB_LIBRARY_PATH=…同样的 pm install -r {path}, 我在adb shell下执行就是可以的。不了解这个是个什么问题
根据具体错误 Google
您好,非常感谢能提供这么一个有用的库。我在开发的一个项目正好需要静默安装的功能。之前采用的是自己通过runtime调用pm命令,现在改用这个了。但是依然会返回:Segmentation fault。我看到你在库里面添加了LD_LIBRARY_PATH(我在其他地方看到这个可能是引起segfault的原因),因此出了这个问题才感觉奇怪。想请教一下。我的设备是root的,但是不是手机,是一款定制的android 4.0.3平台。我在runtime下不先执行su,直接执行命令会返回一条error:pkg:XXX.apk,process.waitfor()返回9。直接执行su pm install …会提示su使用方式不对(在adb shell)下也是。在adb shell下执行pm install可以安装程序。如能解答,不胜感激
public void btnUninstall(View v) { Log.e(“btnUninstall”, “btnUninstall”); PackageUtils.uninstallSilent(this, “com.alexbbb.uploadservice.demo”); } 为什么我点了卸载 提示已经授权 root ,却没卸载成功?
360 骗子 ,原来intel 的root失败换了台arm的root 后测试通过,TKS!
debug 调试,看返回错误原因
冒昧的问一下 如果这个apk下载过程中损坏了 通过你的方法能正确的判断是否安装成功吗,因为我看到返回值都是true
应该会返回INSTALL_FAILED_INVALID_APK
怎么监测多个静默卸载完成啊
跟正常安装一样
只能监测单个应用卸载
hello world的东西,学会自己google