1. 静默应用卸载方式
我们在进行应用卸载的时候,有种使用shell命令的方式进行静默卸载,不起界面,此种方式也可以适用于其他的命令
//代码本质是一种在android代码中使用ProcessBuilder,非常规调用shell命令的方式
String[] args = {
"pm", "uninstall", packageName
};//这个是shell命令中卸载的命令,也可以是其他的命令
String result = null;
ProcessBuilder processBuilder = new ProcessBuilder(args);
Process process = null;
InputStream errIs = null;
InputStream inIs = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read = -1;
process = processBuilder.start();
errIs = process.getErrorStream();
while ((read = errIs.read()) != -1) {
baos.write(read);
}
baos.write('\n');
inIs = process.getInputStream();
while ((read = inIs.read()) != -1) {
baos.write(read);
}
byte[] data = baos.toByteArray();
result = new String(data);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (errIs != null) {
errIs.close();
}
if (inIs != null) {
inIs.close();
}
} catch (Exception e) {
e.printStackTrace();
}
if (process != null) {
process.destroy(); //用完后要销毁
}
}
2. 平台异常不支持
但是此段代码在6.0的平台中会爆出异常
03-02 16:16:31.138 4360 4360 D AndroidRuntime: Calling main entry com.android.commands.pm.Pm
03-02 16:16:31.142 1848 2706 W AppOps : Bad call: specified package null under uid 1000 but it is really -1
03-02 16:16:31.143 4360 4360 E Pm : Error
03-02 16:16:31.143 4360 4360 E Pm : java.lang.SecurityException: Package null does not belong to 1000
03-02 16:16:31.143 4360 4360 E Pm : at android.os.Parcel.readException(Parcel.java:1599)
03-02 16:16:31.143 4360 4360 E Pm : at android.os.Parcel.readException(Parcel.java:1552)
03-02 16:16:31.143 4360 4360 E Pm : at android.content.pm.IPackageInstaller$Stub$Proxy.uninstall(IPackageInstaller.java:448)
03-02 16:16:31.143 4360 4360 E Pm : at com.android.commands.pm.Pm.runUninstall(Pm.java:1614)
03-02 16:16:31.143 4360 4360 E Pm : at com.android.commands.pm.Pm.run(Pm.java:174)
03-02 16:16:31.143 4360 4360 E Pm : at com.android.commands.pm.Pm.main(Pm.java:108)
03-02 16:16:31.143 4360 4360 E Pm : at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
03-02 16:16:31.143 4360 4360 E Pm : at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:251)
03-02 16:16:31.144 4360 4360 I art : System.exit called, status: 1
我们追踪代码,可以看到在6.0的系统中在使用uninstall方法时候会做出shell命令的检测,下面记录一下追踪过程
at com.android.commands.pm.Pm.runUninstall,这段代码可以看做是问题的开始,卸载的shell命令经过一系列转发发到Pm.java中,
public int run(String[] args) throws IOException, RemoteException {
boolean validCommand = false;
...
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
return runInstallAbandon();
}
if ("set-installer".equals(op)) {
return runSetInstaller();
}
if ("uninstall".equals(op)) {
return runUninstall();
}
if ("clear".equals(op)) {
return runClear();
}
....
....
....
}
private int runUninstall() throws RemoteException { //这个方法扔出异常
....
final LocalIntentReceiver receiver = new LocalIntentReceiver();
//这个重点,会调用到mInstaller的卸载方法, 看到mInstaller为IPackageInstaller类型,设计到aidl调用,我们直接代码搜索 IPackageInstaller.Stub 关键字,可以搜到这个uninstall的方法实现,定位到PackageInstallerService.java类
mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
receiver.getIntentSender(), userId);
....
}
public void uninstall(String packageName, String callerPackageName, int flags,
IntentSender statusReceiver, int userId) {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
//这个是异常导致点,会做uid检测,Process.SHELL_UID=2000,这是shell用户,Process.ROOT_UID是表示root用户,我们用adb或者串口是这个进程,但是我们正常用户uid不等于0或者2000,所以需要checkPackage
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
}
....
}
public void checkPackage(int uid, String packageName) {
try {
if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
throw new SecurityException(
"Package " + packageName + " does not belong to " + uid);
}
} catch (RemoteException e) {
throw new SecurityException("Unable to verify package ownership", e);
}
}
是这个地方扔出的安全异常,log可以对上Package null does not belong to 1000,(这里1000对应系统进程,uid为1000),扔出异常是因为runUninstall中的
mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
receiver.getIntentSender(), userId);
callerPackageName 这个参数设为了null,所以检查包名是否属于这个uidGroup中为否,扔出了SecurityException
这是6.0平台不准使用这种方式进行uninstall的操作,所以我们可以注掉这个检测,规避这种异常,查看了一下,应该只有6.0会存在这个异常