最近在做一个apk分析器,里面可以解析系统中所有安装app的信息,并提供组内开发的apk文件下载、静默安装(包括降级安装),其中在降级安装中难度较大,在Android4.4与Android 8的解决方案不同,其他版本没有做测试。在此之前,打算聊聊adb的安装方式
目前暂时支持已经签过系统签名文件的apk,非系统签名暂时不支持。
adb安装apk常用命令如下:
adb install G:\demo.apk
即install后面接包在电脑上的路径,这里要确保已经通过adb连接到设备,常用以下命令连接,确保电脑与设备处于同一个局域网:
adb connect 设备ip
如果需要替换原来的应用,上面的安装命令是行不通的,需要加上“-r”,即替换原来的应用:
adb install -r G:\demo.apk
那如果是降级安装呢?再加“-d”:
adb install -r -d G:\demo.apk
这里的“r”指的是“replace”,替换原来的应用;“-d”指的是“downgrade”,降级安装
这不是成了吗?不对,这是通过adb命令,在Android应用中无法使用该命令,那么该如何解决呢?这里要引出另一个概念“pm”
“pm” 是指 “packageManager”,Android自带的PackageInstaller是通过pm来执行具体的安装工作,具体流程这里就不做分析了。我们来看如何直接通过pm来安装apk,首先进入shell模式,然后就可以使用pm命令:
adb shell pm install /data/data/demo.apk
这里的apk路径是在设备中的路径,同理如果要实现降级安装:
pm install -r -d /data/data/demo.apk
哈哈,是不是感觉在Android应用端实现该命令就很简单了?如下:
String cmd = "pm install -r -d /data/data/demo.apk" Runtime run = Runtime.getRuntime(); Process process = run.exec(cmd);
然后就failure了~~
该命令不是每一个应用都可以执行的,需要系统签名,将应用声明为系统应用。在Androidmanifest中配置:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="xxx.xxx.xxxx" android:sharedUserId="android.uid.system"> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> </manifest>
在run一次,ok,在Android4.4以及之前的版本没问题,但是在更高的版本,例如Android 7 就不行了,这里需要稍微修改一下:
pm install -r -d -i packageName --user 0 /data/data/demo.apk
这里的packageName是指调用这行命令的应用的包名
需要注意的是,runtime执行命令行会阻塞线程,因此需要在子线程中执行
那么,我们应该如何判断是否安装成功呢?很简单了,通过runtime执行返回的process就可以拿到输出的结果,完整代码如下:
public void install(File apkFile) { String cmd = ""; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { cmd = "pm install -r -d " + apkFile.getAbsolutePath(); } else { cmd = "pm install -r -d -i packageName --user 0 " + apkFile.getAbsolutePath(); } Runtime runtime = Runtime.getRuntime(); try { Process process = runtime.exec(""); InputStream errorInput = process.getErrorStream(); InputStream inputStream = process.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String error = ""; String result = ""; String line = ""; while ((line = bufferedReader.readLine()) != null) { result += line; } bufferedReader = new BufferedReader(new InputStreamReader(errorInput)); while ((line = bufferedReader.readLine()) != null) { error += line; } if(result.equals("Success")){ Log.i(TAG, "install: Success"); }else{ Log.i(TAG, "install: error"+error); } } catch (IOException e) { e.printStackTrace(); } }
该方案需要获取系统权限,进行系统签名。当然,大家也可以在模拟器上试一试,拿到模拟器系统版本对应的源码,找到这3个文件
SignApk.jar 目录:/out/host/linux-x86/framework/signapk.jar
platform.x509.pem 目录:/build/target/product/security/platform.x509.pem
platform.pk8 目录:/build/target/product/security/platform.pk8
将这三个文件copy到同一个目录下,在该目录下执行:
java -jar SignApk.jar platform.x509.pem platform.pk8 旧的apk.apk 生成的apk.apk
即可以得到系统签名文件