最近需要实现Android应用的静默安装,在网上看了不少帖子,最后在root权限下实现对应用的静默安装和卸载,现在就整个实现的过程做一个总结。

一.第一种方案
第一种方案参考了源码中/packages/apps/PackageInstaller的实现方式,实现的主要代码如下:

importjava.io.File;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.pm.PackageInfo;
importandroid.content.pm.PackageManager;
importandroid.content.pm.IPackageInstallObserver;
importandroid.content.pm.PackageManager.NameNotFoundException;
importandroid.content.pm.PackageParser;
importandroid.net.Uri;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.util.DisplayMetrics;
importandroid.util.Log;
importandroid.os.FileUtils;
 
publicclass MyPackageInstaller {
privatestatic final String PACKAGE_NAME = "test.installservice";
privatefinal int INSTALL_COMPLETE = 1;
privateContext context;
Uri mPackageURI;
privatePackageParser.Package mPkgInfo;
 
privateHandler mHandler = newHandler() {
publicvoid handleMessage(Message msg) {
switch(msg.what) {
caseINSTALL_COMPLETE:
// finish the activity posting result
//setResultAndFinish(msg.arg1);
break;
default:
break;
}
}
};
 
voidsetResultAndFinish(intretCode) {
// Intent data = new Intent();
// setResult(retCode);
// finish();
}
 
publicMyPackageInstaller(Context c) {
this.context = c;
}
 
publicvoid installPackage() {
intinstallFlags = 0;
PackageManager pm = context.getPackageManager();
try{
PackageInfo pi = pm.getPackageInfo(
mPkgInfo.applicationInfo.packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if(pi != null) {
// installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
installFlags |= 2;
}
}catch(NameNotFoundException e) {
}
 
String array[] = null;
try{
Runtime.getRuntime().exec("chmod 777 /data/data/" + PACKAGE_NAME);
Runtime.getRuntime().exec(
"chmod 777 /data/data/" + PACKAGE_NAME + "/files");
array = this.mPackageURI.toString().split("/");
System.out.println("array[last]->"+ array[array.length - 1]);
Runtime.getRuntime().exec(
"chmod 777 /data/data/" + PACKAGE_NAME + "/files/"
+ array[array.length - 1]);
}catch(IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 
PackageInstallObserver observer = newPackageInstallObserver();
pm.installPackage(mPackageURI, observer, installFlags, null);
// context.deleteFile(array[array.length-1]);
}
 
classPackageInstallObserver extendsIPackageInstallObserver.Stub {
publicvoid packageInstalled(String packageName, intreturnCode) {
Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);
msg.arg1 = returnCode;
mHandler.sendMessage(msg);
}
}
 
privateFile createTempPackageFile(String filePath) {
File tmpPackageFile;
inti = filePath.lastIndexOf("/");
String tmpFileName;
if(i != -1) {
tmpFileName = filePath.substring(i + 1);
}else{
tmpFileName = filePath;
}
FileOutputStream fos;
try{
// MODE_WORLD_READABLE=1
fos = context.openFileOutput(tmpFileName, 1);
}catch(FileNotFoundException e1) {
Log.e("Installer","Error opening file " + tmpFileName);
returnnull;
}
try{
fos.close();
}catch(IOException e) {
Log.e("Installer","Error opening file " + tmpFileName);
returnnull;
}
tmpPackageFile = context.getFileStreamPath(tmpFileName);
File srcPackageFile = newFile(filePath);
if(!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) {
returnnull;
}
returntmpPackageFile;
}
 
publicvoid makeTempCopyAndInstall(Uri mPackageURI) {
mPkgInfo = getPackageInfo(mPackageURI);
System.out.println("package="+ mPkgInfo.applicationInfo.packageName);
System.out.println("copy file=" + mPackageURI.getPath());
File mTmpFile = createTempPackageFile(mPackageURI.getPath());
if(mTmpFile == null) {
// display a dialog
Log.e("Installer",
"Error copying file locally. Failed Installation");
// showDialogInner(DLG_OUT_OF_SPACE);
return;
}
this.mPackageURI = Uri.parse("file://"+ mTmpFile.getPath());
}
 
publicPackageParser.Package getPackageInfo(Uri packageURI) {
finalString archiveFilePath = packageURI.getPath();
PackageParser packageParser = newPackageParser(archiveFilePath);
File sourceFile = newFile(archiveFilePath);
DisplayMetrics metrics = newDisplayMetrics();
metrics.setToDefaults();
returnpackageParser.parsePackage(sourceFile, archiveFilePath, metrics,
0);
}
}


在程序中的调用方式:this为Context,path为安装包的绝对路径

 
MyPackageInstaller mpi = newMyPackageInstaller(this);
mpi.makeTempCopyAndInstall(Uri.parse(path));
mpi.installPackage();



这种方式需要在源码下面编译apk,并将apk放入/system/app目录下面。

二.通过shell命令实现
首先,在java中实现安装和卸载apk的命令

publicclass PackageInstaller {
 
publicvoid unInstallApp(String packageName){
try{
Runtime.getRuntime().exec("pm uninstall "+packageName);
}catch(IOException e) {
e.printStackTrace();
}
}
 
publicvoid installApp(String appPath){
try{
Runtime.getRuntime().exec("pm install "+appPath);
}catch(IOException e) {
e.printStackTrace();
}
}
 
publicvoid reInstallApp(String appPath){
try{
Runtime.getRuntime().exec("pm install -r "+appPath);
}catch(IOException e) {
e.printStackTrace();
}
}
 
}
 
 


然后再源码环境下将该java程序编译为jar包
2.将编译好的jar包放入程序的assets目录下面,通过以下代码在程序中将该jar文件拷贝到/data/data/package/files/目录下面

try{
AssetManager assetManager = context.getResources().getAssets();
InputStream in = assetManager.open(JAR_NAME);
if(in == null) {
return;
}
intlength = in.available();
bytefileByte[] = newbyte[length];
in.read(fileByte,0, fileByte.length);
in.close();
OutputStream out = context.openFileOutput(JAR_NAME,
Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
out.write(fileByte);
out.close();
}catch(Exception e) {
e.printStackTrace();
}


在有root权限的情况下,可以在shell中执行该jar包来进行安装和卸载:

String exportClassPath = "export CLASSPATH=/data/data/"
+ context.getPackageName() + "/files/installpackagejar.jar";
String INSTALL_ACTION_CMD = " exec app_process /system/bin packageName.StartMain install ";



publicboolean installApp(String path) {
File temp = newFile(path);
if(!temp.exists())
returnfalse;
String cmd[] = { exportClassPath, INSTALL_ACTION_CMD + path };
try{
consoleExec(cmd);
}catch(IOException e) {
e.printStackTrace();
returnfalse;
}
returntrue;
}

 

privatevoid consoleExec(String[] cmd) throwsIOException {
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = newDataOutputStream(process.getOutputStream());
for(inti = 0; i < cmd.length; i++) {
os.writeBytes(cmd<i> + "\n");
}
os.writeBytes("exit\n");
os.flush();
os.close();
}