Android-静默安装与卸载实现

简述

本文介绍如何使用安卓的隐藏API,实现应用的安装与删除功能。

PackageManager的框架

PackageManager框架介绍

整体结构如下所示,PackageManager为接口文件,ApplicationPackageManager为PackageManager的实现类,而实际完成应用管理的是PackageManagerService。

服务端为:

在这里插入图片描述

客户端为:

在这里插入图片描述

在该框架结构中,涉及到了客户端与服务器的进程间通信,在安卓源码中是使用AIDL解决进程间通信的问题。

文件所在位置

文件名位置
PackageManager.javaframeworks\base\core\java\android\content\pm
ApplicationPackageManager.javaframeworks\base\core\java\android\app
IPackageManager.aidlframeworks\base\core\java\android\content\pm
PackageManagerService.javaframeworks\base\services\java\com\android\server\pm

代码分析

PackageManager对象是通过Context的getPackageManager方法获得的,查看ContextImpl.java(位于frameworks\base\core\java\android\app下)的getPackageManager方法,可以发现返回的为ApplicationPackageManager对象,同时还带入了IPackageManager对象作为参数。

在这里插入图片描述

在ApplicationPackageManager中,我们可以看到其实真正的操作都是通过mPM对象完成的。
在这里插入图片描述
在这里插入图片描述

IPackageManager的对象是通过ActivityThread(安卓主线程,位于frameworks\base\core\java\android\app下)的getPackageManager方法获得的,查看该方法。其中,通过ServiceManager.getService(“package”)获得PackageManagerService,通过调用IPackageManager.Stub.asInterface(b)得到IPackageManager的代理类Proxy。

在这里插入图片描述

APK安装与卸载的返回值

PackageManager中一共包含了38种安装返回值,以及4种卸载返回值。

安装返回值

属性名码值说明
INSTALL_SUCCEEDED1安装成功
INSTALL_FAILED_ALREADY_EXISTS-1应用已经存在
INSTALL_FAILED_INVALID_APK-2安装包无效
INSTALL_FAILED_INVALID_URI-3URI无效
INSTALL_FAILED_INSUFFICIENT_STORAGE-4存储空间不足
INSTALL_FAILED_DUPLICATE_PACKAGE-5存在同名文件
INSTALL_FAILED_NO_SHARED_USER-6shared用户不存在
INSTALL_FAILED_UPDATE_INCOMPATIBLE-7与之前已经安装的同名应用签名不同
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE-8与之前已经安装的同名应用shared用户不同
INSTALL_FAILED_MISSING_SHARED_LIBRARY-9使用了无效的共享库
INSTALL_FAILED_REPLACE_COULDNT_DELETE-10无法删除之前的安装
INSTALL_FAILED_DEXOPT-11优化和验证dex文件时失败,原因可能是存储空间不足或验证失败。
INSTALL_FAILED_OLDER_SDK-12SDK版本过低
INSTALL_FAILED_CONFLICTING_PROVIDER-13包含在该系统中已经存在的content provider
INSTALL_FAILED_NEWER_SDK-14SDK版本过新
INSTALL_FAILED_TEST_ONLY-15只用于测试
INSTALL_FAILED_CPU_ABI_INCOMPATIBLE-16应用包含了不与CPU ABI兼容的native代码
INSTALL_FAILED_MISSING_FEATURE-17应用中使用了不可用功能
INSTALL_FAILED_CONTAINER_ERROR-18无法接入外部媒体受保护内容
INSTALL_FAILED_INVALID_INSTALL_LOCATION-19无法在指定的位置上安装该应用
INSTALL_FAILED_MEDIA_UNAVAILABLE-20Media无效
INSTALL_FAILED_VERIFICATION_TIMEOUT-21安装超时
INSTALL_FAILED_VERIFICATION_FAILURE-22验证失败
INSTALL_FAILED_PACKAGE_CHANGED-23安装包名与应用中规定的不同
INSTALL_FAILED_UID_CHANGED-24与之前已经安装的同名应用UID不同
INSTALL_FAILED_VERSION_DOWNGRADE-25应用版本低于之前已经安装的同名应用
INSTALL_PARSE_FAILED_NOT_APK-100非.apk结尾
INSTALL_PARSE_FAILED_BAD_MANIFEST-101缺少AndroidManifest.xml文件
INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION-102解析器发生非预期错误
INSTALL_PARSE_FAILED_NO_CERTIFICATES-103解析器为发现相关证书
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES-104解析器发现不一致证书
INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING-105解析器在解码某个文件是发生CertificateEncodingException
INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME-106解析器解释到无效错误包名
INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID-107解析器遇到错误的shared用户ID。
INSTALL_PARSE_FAILED_MANIFEST_MALFORMED-108解析器遇到结构上的问题
INSTALL_PARSE_FAILED_MANIFEST_EMPTY-109解析器未发现任何actionable(instrumentation or application)标签
INSTALL_FAILED_INTERNAL_ERROR-110因为系统问题无法安装
INSTALL_FAILED_USER_RESTRICTED-111无法安装因为用户被限制安装

卸载返回值

属性名码值说明
DELETE_SUCCEEDED1卸载成功
DELETE_FAILED_INTERNAL_ERROR-1卸载失败,原因不明
DELETE_FAILED_DEVICE_POLICY_MANAGER-2卸载失败,DevicePolicy manager的实现
DELETE_FAILED_USER_RESTRICTED-3卸载失败,用户受限

静默安装与卸载的实现

安装流程

在这里插入图片描述

在这里插入图片描述

Root情况下

通过执行底层Linux命令

在机器已经root过的情况下,我们可以利用root用户的权限,使用底层Linux命令进行应用的安装与卸载操作。

public String do_exec(String cmd) {
        try {
            Process exeEcho = Runtime.getRuntime().exec("su");
            DataOutputStream os = new DataOutputStream(exeEcho.getOutputStream());
            os.writeBytes(cmd);
            os.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return cmd;
    }

注:这种方法实现应用的安装与卸载应用必须确保机器已经被root过。

非Root情况下

自行编译代码

通过删除掉PackageManager中被@hide注释的方法并编译新的jar包进行使用。

反射

通过反射ApplicationPackageManager对象的安装和卸载方式实现。注意:不能反射PackageManager.java的方法,否则会出现空指针错误,因为PackageManager.java只是接口文件。

代码如下:

public static void installSilentWithReflection(Context context, String filePath, String packageName) {
	try {
		PackageManager packageManager = context.getPackageManager();
		Method method = packageManager.getClass().getDeclaredMethod("installPackage",
				new Class[] {Uri.class, IPackageInstallObserver.class, int.class, String.class} );
		method.setAccessible(true);
		File apkFile = new File(filePath);
		Uri apkUri = Uri.fromFile(apkFile);

		method.invoke(packageManager, new Object[] {apkUri, new IPackageInstallObserver.Stub() {
			@Override
			public void packageInstalled(String pkgName, int resultCode) throws RemoteException {
				Log.d(TAG, "packageInstalled = " + pkgName + "; resultCode = " + resultCode) ;
			}
		}, Integer.valueOf(2), packageName});
		//PackageManager.INSTALL_REPLACE_EXISTING = 2;
	} catch (NoSuchMethodException e) {
		e.printStackTrace();
	} catch (Exception e) {
		e.printStackTrace();
	}
}

public static void deleteSilentWithReflection(Context context, String packageName) {
	try {
		PackageManager packageManager = context.getPackageManager();
		Method method = packageManager.getClass().getDeclaredMethod("deletePackage",
				new Class[] {String.class, IPackageDeleteObserver.class, int.class} );
		method.setAccessible(true);
		int DELETE_ALL_USERS = 0x00000002;

		method.invoke(packageManager, new Object[] {packageName, new IPackageDeleteObserver.Stub() {
			@Override
			public void packageDeleted(String packageName, int returnCode) throws RemoteException {
				Log.d(TAG, "packageDelete = " + packageName) ;
			}
		}, DELETE_ALL_USERS });
	} catch (NoSuchMethodException e) {
		e.printStackTrace();
	} catch (Exception e) {
		e.printStackTrace();
	}
}

注:通过反射实现应用的安装与卸载的方式实现应用安装与卸载比较简单,但是如果需要调用多个方法则需要反射多次,这种条件下使用反射会变得异常繁琐。

AIDL

通过AIDL的方式,实现进程间的通信,得到IPackageManager的代理类Proxy进行操作。

首先,要保证项目中包含以下AIDL文件,这些文件都可以在frameworks\base\core\java\android\content\pm中直接复制,其中IPackageDeleteObserver.aidl、IPackageInstallObserver.aidl和IPackageManager.aidl是实现安装与卸载不可或缺的,其余文件可以除去:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivNLYWnG-1574062319922)(https://kcms.konkawise.com/upload/201909251152008190.png)]

代码如下:

/**
 * 表示安装过程中是否锁定
 */
public static final int INSTALL_FORWARD_LOCK = 0x00000001;

/**
 * 表示是否替换安装包
 */
public static final int INSTALL_REPLACE_EXISTING = 0x00000002;

/**
 * 是否是测试安装包
 */
public static final int INSTALL_ALLOW_TEST = 0x00000004;

/**
 * 指定本次安装必须在SDCard中
 */
public static final int INSTALL_EXTERNAL = 0x00000008;

/**
 * 指定本次安装必须在内部存储空间中
 */
public static final int INSTALL_INTERNAL = 0x00000010;

/**
 * 指定本次安装由ADB起始
 */
public static final int INSTALL_FROM_ADB = 0x00000020;

/**
 * 安装对于所有用户立即可见
 */
public static final int INSTALL_ALL_USERS = 0x00000040;

/**
 * 允许安装比当前版本低的安装包
 */
public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;

/**
 * 不删除用户数据
 */
public static final int DELETE_KEEP_DATA = 0x00000001;

/**
 * 不保存用户数据
 */
public static final int DELETE_ALL_USERS = 0x00000002;

/**
 * 可卸载系统应用
 */
public static final int DELETE_SYSTEM_APP = 0x00000004;

private static MyPackageManager instance;

private static IPackageManager mIPackageManager = null;

private MyPackageManager() {
	Class<?> forName = null;
	try {
		forName = Class.forName("android.os.ServiceManager");
		Method method = forName.getMethod("getService", String.class);
		IBinder iBinder = (IBinder) method.invoke(null, "package");
		mIPackageManager = IPackageManager.Stub.asInterface(iBinder);
	} catch (ClassNotFoundException e) {
		e.printStackTrace();
	} catch (NoSuchMethodException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	} catch (InvocationTargetException e) {
		e.printStackTrace();
	}
}

public static MyPackageManager getInstance() {
	if (instance == null) {
		instance = new MyPackageManager();
	}
	return instance;
}

public void installPackage(Uri uri, IPackageInstallObserver iPackageInstallObserver, int flag, String installerPackageName) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, RemoteException {
	mIPackageManager.installPackage(uri, iPackageInstallObserver, flag, installerPackageName);
}


public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, RemoteException {
	mIPackageManager.deletePackageAsUser(packageName, observer, getUserId(), flags);
}

public ApplicationInfo getApplicationInfo(String packageName) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, RemoteException {
	return mIPackageManager.getApplicationInfo(packageName, 0, getUserId());
}

private int getUserId() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
	Class UserHandle = Class.forName("android.os.UserHandle");
	Method method = UserHandle.getDeclaredMethod("myUserId", null);
	return method.invoke(null, null) == null ? -1 : (int) method.invoke(null, null);
}

代码中,因为需要用到android.os.ServiceManager的getService方法来取得PackageManagerService,所以还需要通过反射来调用getService。

forName = Class.forName("android.os.ServiceManager");
Method method = forName.getMethod("getService", String.class);
IBinder iBinder = (IBinder) method.invoke(null, "package");
mIPackageManager = IPackageManager.Stub.asInterface(iBinder);

注:通过AIDL的方式实现应用的安装与卸载比较复杂,但是成功取得IPackageManager的代理类后可以直接调用多个方法。

ProcessBuilder

ProcessBuilder类是J2SE 1.5在java.lang中新添加的一个新类,此类用于创建操作系统进程,它提供一种启动和管理进程的方法。通过ProcessBuilder,可以利用Linux pm实现应用的安装与卸载。

代码如下:

public static int deleteSilent(String packageName) {
	if (packageName == null || packageName.length() == 0 || packageName.trim().equals("")) {
		return 1;
	}

	String[] args = {"pm", "uninstall", packageName};
	ProcessBuilder processBuilder = new ProcessBuilder(args);
	Process process = null;
	BufferedReader successResult = null;
	BufferedReader errorResult = null;
	StringBuilder successMsg = new StringBuilder();
	StringBuilder errorMsg = new StringBuilder();
	int result;
	try {
		process = processBuilder.start();
		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 (successResult != null) {
				successResult.close();
			}
			if (errorResult != null) {
				errorResult.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (process != null) {
			process.destroy();
		}
	}

	// TODO should add memory is not enough here
	if (successMsg.toString().contains("Success") || successMsg.toString().contains("success")) {
		result = 0;
	} else {
		result = 2;
	}
	Log.d("test-test", "successMsg:" + successMsg + ", ErrorMsg:" + errorMsg);
	return result;
}

public static int installSilent(String filePath) {
	File file = new File(filePath);
	if (filePath == null || filePath.length() == 0 || file == null || file.length() <= 0 || !file.exists() || !file.isFile()) {
		return 1;
	}

	String[] args = {"pm", "install", "-r", filePath};
	ProcessBuilder processBuilder = new ProcessBuilder(args);
	Process process = null;
	BufferedReader successResult = null;
	BufferedReader errorResult = null;
	StringBuilder successMsg = new StringBuilder();
	StringBuilder errorMsg = new StringBuilder();
	int result;
	try {
		process = processBuilder.start();
		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 (successResult != null) {
				successResult.close();
			}
			if (errorResult != null) {
				errorResult.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (process != null) {
			process.destroy();
		}
	}

	// TODO should add memory is not enough here
	if (successMsg.toString().contains("Success") || successMsg.toString().contains("success")) {
		result = 0;
	} else {
		result = 2;
	}
	Log.d("test-test", "successMsg:" + successMsg + ", ErrorMsg:" + errorMsg);
	return result;
}

注:通过ProcessBuilder不仅仅可以使用pm操作,还可以使用am的操作,功能非常的强大。然而问题还是需要给每一个功能写特定的实现。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Inno Setup是一款流行的Windows安装程序创建工具,支持静默安装卸载静默安装指的是在用户操作的情况下,自动进行软件安装而无需用户进行任何操作。以下是如何使用Inno Setup进行静默安装卸载的方法: 静默安装: 1.首先,确保已经准备好要安装的软件的所有必要文件和文件夹。 2.创建一个包含安装程序信息的脚本文件,可以使用Inno Setup提供的脚本编辑器或其他文本编辑器来编写。 3.在脚本文件中,使用[Setup]部分的相应选项配置静默安装模式。例如,可以将"DisableStartupPrompt"选项设置为yes,这将禁用任何与用户操作相关的提示消息。 4.保存和编译脚本,生成安装程序。 5.在命令行中执行安装程序,并使用"/VERYSILENT"和"/NORESTART"参数以及其他可能的参数来启用静默安装模式。例如,"setup.exe /VERYSILENT /NORESTART"。 6.安装程序将在后台运行,进行静默安装卸载: 1.确定要卸载的软件的安装位置和卸载程序的名称。 2.打开命令提示符或PowerShell窗口,并导航到安装目录。 3.在命令行中输入卸载程序的名称,通常为"unins000.exe"或类似名称。 4.使用"/VERYSILENT"参数来启用静默卸载模式。例如,"unins000.exe /VERYSILENT"。 5.卸载程序将在后台运行,进行静默卸载。 6.卸载完成后,软件将从系统中完全移除。 总结而言,Inno Setup可以通过配置脚本文件和使用适当的参数,在静默模式下进行软件的安装卸载。这对于需要批量安装或远程安装的情况下非常有用,同时也减少了用户操作的繁琐。 ### 回答2: Inno Setup 是一个常用的安装程序制作工具,可以帮助开发者创建 Windows 平台下的安装程序。而静默安装卸载则是 Inno Setup 提供的两个重要功能。 静默安装(Silent Installation)是指在用户无需进行任何交互的情况下进行安装。通过传递命令行参数给 Inno Setup 执行文件,可以实现静默安装。一般来说,只需要在执行文件后面添加 /SILENT 或 /VERYSILENT 参数就可以实现静默安装。/SILENT 参数会显示进度条,/VERYSILENT 参数则完全不显示任何界面。 静默卸载(Silent Uninstallation)是指对已经安装的软件进行无需用户交互的卸载。同样地,可以通过添加命令行参数给 Inno Setup 卸载文件实现静默卸载。一般来说,在卸载文件后面添加 /SILENT 或 /VERYSILENT 参数即可实现静默卸载。 通过使用 Inno Setup 的静默安装卸载功能,可以实现自动化的软件发布和管理。开发者可以在无需用户干预的情况下,批量安装卸载软件,提高效率并减少错误。这对于企业环境中的大规模软件部署和维护尤为重要。 总而言之,Inno Setup 提供了静默安装卸载的功能,使得软件部署和维护更加便捷高效。通过合理运用这两个功能,开发者可以轻松实现自动化的软件安装卸载,提高工作效率。 ### 回答3: Inno Setup 是一款功能强大的安装程序制作工具,也可以通过静默安装的方式来安装卸载软件。 静默安装是一种在用户无感知的情况下自动完成安装过程的方式。在使用 Inno Setup 进行静默安装时,我们可以通过命令行参数来实现。具体步骤如下: 1. 打开命令提示符或者脚本编辑器。 2. 切换到 Inno Setup 的安装程序所在的目录。 3. 输入以下命令进行静默安装: ``` Setup.exe /SILENT /NORESTART ``` `/SILENT` 参数表示以静默模式运行安装程序,安装过程中不会显示任何界面和提示信息。 `/NORESTART` 参数表示安装完成后不自动重启计算机。 4. 等待一段时间,直到安装程序自动完成安装。 同样地,我们也可以使用相同的方式进行静默卸载。 1. 打开命令提示符或者脚本编辑器。 2. 切换到 Inno Setup 的安装程序所在的目录。 3. 输入以下命令进行静默卸载: ``` Setup.exe /SILENT /UNINSTALL ``` `/SILENT` 参数表示以静默模式运行卸载程序,卸载过程中不会显示任何界面和提示信息。 `/UNINSTALL` 参数表示执行卸载操作。 4. 等待一段时间,直到卸载程序自动完成卸载。 通过以上步骤,我们可以实现使用 Inno Setup 进行静默安装卸载的功能。这种方式可以有效减少用户的操作,提高安装卸载的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值