最近在做有关静默安装和静默卸载的功能,6.0之前可以使用:
需要的权限:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
卸载
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
需要注意的是,需要系统签名 。
系统签名后使用adb命令查看是否有系统权限,命令如下:
adb shell dumpsys package 你的应用包名
如下图:
String[] args = null;
if (loc == InsLoc.EXT) {
args = new String[]{ _pm, _install, __r, __s, apkAbsolutePath };
} else if (loc == InsLoc.INT){
args = new String[]{ _pm, _install, __r, __f, apkAbsolutePath };
} else {
args = new String[]{ _pm, _install, __r, apkAbsolutePath };
}
String result = "";
ProcessBuilder processBuilder = new ProcessBuilder(args);
Process process = null;
DataInputStream errIs = null;
DataInputStream inIs = null;
try {
process = processBuilder.start();
MyLog.d(TAG, "----install before waitfor----");
int r = process.waitFor();
MyLog.d(TAG, "----install after waitfor----r:" + r);
errIs = new DataInputStream(process.getErrorStream());
String line = null;
while ((line = errIs.readLine()) != null) {
MyLog.d(TAG, "----err line:[" + line + "]----");
if (line.contains(_Failure)) {
result = line;
}
}
inIs = new DataInputStream(process.getInputStream());
line = null;
while ((line = inIs.readLine()) != null) {
MyLog.d(TAG, "----std line:[" + line + "]----");
if (line.contains(_Success)) {
result = line;
}
}
} catch (IOException e) {
MyLog.w(TAG, "----install ERROR:" + e.getMessage());
} catch (Exception e) {
MyLog.w(TAG, "----install ERROR:" + e.getMessage());
} finally {
try {
if (errIs != null) {
errIs.close();
}
if (inIs != null) {
inIs.close();
}
} catch (IOException e) {
}
if (process != null) {
process.destroy();
}
}
能够正常静默,必须使用root样机。
当放到7.0样机上时,发现静默不了:出现java.lang.SecurityException: Package xxxx does not belong to 10054异常。通过不断的google和度娘,最终使用下面的方法解决:
String result = "";
// ProcessBuilder processBuilder = new ProcessBuilder(args);
Process process = null;
DataInputStream errIs = null;
DataInputStream inIs = null;
try {
process = Runtime.getRuntime().exec("pm install -i "+packageName+" --user 0 "+apkAbsolutePath);
MyLog.d(TAG, "----install before waitfor----");
int r = process.waitFor();
MyLog.d(TAG, "----install after waitfor----r:" + r);
errIs = new DataInputStream(process.getErrorStream());
String line = null;
while ((line = errIs.readLine()) != null) {
MyLog.d(TAG, "----err line:[" + line + "]----");
if (line.contains(_Failure)) {
result = line;
}
}
inIs = new DataInputStream(process.getInputStream());
line = null;
while ((line = inIs.readLine()) != null) {
MyLog.d(TAG, "----std line:[" + line + "]----");
if (line.contains(_Success)) {
result = line;
}
}
} catch (IOException e) {
MyLog.w(TAG, "----install ERROR:" + e.getMessage());
} catch (Exception e) {
MyLog.w(TAG, "----install ERROR:" + e.getMessage());
} finally {
try {
if (errIs != null) {
errIs.close();
}
if (inIs != null) {
inIs.close();
}
} catch (IOException e) {
}
if (process != null) {
process.destroy();
}
}
return result;
值得注意的是,上面的package指的是自身应用的package。当时就因为这个package踩了不少坑。
有关静默卸载:6.0以下使用:
String[] args = null;
if (keepData){
args = new String[]{ _pm, _uninstall, __k, packageName };
}else {
args = new String[]{ _pm, _uninstall, packageName };
}
String result = "";
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);
MyLog.i(TAG, "----unInstallPackage exec cmd finish:[" + result.trim() + "]");
} catch (IOException e) {
MyLog.w(TAG, "----unInstallPackage ERR:" + e.getMessage(), e);
} catch (Exception e) {
MyLog.w(TAG, "----unInstallPackage ERR:" + e.getMessage(), e);
} finally {
try {
if (errIs != null) {
errIs.close();
}
if (inIs != null) {
inIs.close();
}
} catch (IOException e) {
MyLog.w(TAG, "----unInstallPackage WARN:" + e.getClass().getName() + "--" + e.getMessage(), e);
}
if (process != null) {
process.destroy();
}
}
return result;
能够正常在root样机下卸载,但是到了6.0以上的时候。又坑爹了。经过研究可以使用反射:
try {
PackageManager pm = context.getPackageManager();
Method[] methods = pm != null ? pm.getClass().getDeclaredMethods() : null;
Method mDel = null;
if (methods != null && methods.length > 0) {
for (Method method : methods) {
if (method.getName().toString().equals("deletePackage")) {
mDel = method;
break;
}
}
}
if (mDel != null) {
mDel.setAccessible(true);
mDel.invoke(pm, pkgName,null, 0);
}
} catch (Exception e) {
}
今天又尝试使用了android9.0使用静默安装,又出现了问题,貌似pm install不给用了,于是便有了以下的代码,封装好了,直接拿去用
package com.project.readdata.utils;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class InstallPackageUtils {
private static String TAG = "carden" + InstallPackageUtils.class.getSimpleName();
private PackageInstaller.SessionCallback mSessionCallback;
private Context mContext;
private int mSessionId = -1;
public static InstallPackageUtils mInstallPackageUitls;
public static InstallPackageUtils getInstance(Context context) {
if (mInstallPackageUitls == null) {
mInstallPackageUitls = new InstallPackageUtils(context);
}
return mInstallPackageUitls;
}
public InstallPackageUtils(Context mContext) {
this.mContext = mContext;
mSessionCallback = new InstallSessionCallback();
mContext.getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
}
/**
* 适配android9的安装方法。
* 全部替换安装
*/
public String installApp(String apkFilePath) {
String success = "fail";
Log.d(TAG, "installApp()------->" + apkFilePath);
File apkFile = new File(apkFilePath);
if (!apkFile.exists()) {
Log.d(TAG, "文件不存在");
}
PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(apkFilePath, PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
if (packageInfo != null) {
String packageName = packageInfo.packageName;
int versionCode = packageInfo.versionCode;
String versionName = packageInfo.versionName;
Log.d("ApkActivity", "packageName=" + packageName + ", versionCode=" + versionCode + ", versionName=" + versionName);
}
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams sessionParams
= new PackageInstaller.SessionParams(PackageInstaller
.SessionParams.MODE_FULL_INSTALL);
Log.d(TAG, "apkFile length" + apkFile.length());
sessionParams.setSize(apkFile.length());
try {
mSessionId = packageInstaller.createSession(sessionParams);
} catch (IOException e) {
e.printStackTrace();
}
Log.d(TAG, "sessionId---->" + mSessionId);
if (mSessionId != -1) {
boolean copySuccess = onTransfesApkFile(apkFilePath);
Log.d(TAG, "copySuccess---->" + copySuccess);
if (copySuccess) {
boolean isSuccess = execInstallAPP();
success = isSuccess ? "Success" : "fail";
Log.d(TAG, "静默成功---" + isSuccess);
}
}
return success;
}
/**
* 通过文件流传输apk
*
* @param apkFilePath
* @return
*/
private boolean onTransfesApkFile(String apkFilePath) {
Log.d(TAG, "---------->onTransfesApkFile()<---------------------");
InputStream in = null;
OutputStream out = null;
PackageInstaller.Session session = null;
boolean success = false;
try {
File apkFile = new File(apkFilePath);
session = mContext.getPackageManager().getPackageInstaller().openSession(mSessionId);
out = session.openWrite("base.apk", 0, apkFile.length());
in = new FileInputStream(apkFile);
int total = 0, c;
byte[] buffer = new byte[1024 * 1024];
while ((c = in.read(buffer)) != -1) {
total += c;
out.write(buffer, 0, c);
}
session.fsync(out);
Log.d(TAG, "streamed " + total + " bytes");
success = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != session) {
session.close();
}
try {
if (null != out) {
out.close();
}
if (null != in) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return success;
}
/**
* 执行安装并通知安装结果
*/
private boolean execInstallAPP() {
Log.d(TAG, "--------------------->execInstallAPP()<------------------");
PackageInstaller.Session session = null;
try {
session = mContext.getPackageManager().getPackageInstaller().openSession(mSessionId);
Intent intent = new Intent(mContext, InstallResultReceiver.class);
intent.putExtra("flag", 1);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
1, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (null != session) {
session.close();
}
}
}
/**
* 根据包名卸载应用
*
* @param packageName
*/
public String uninstall(String packageName) {
try {
Intent broadcastIntent = new Intent(mContext, InstallResultReceiver.class);
broadcastIntent.putExtra("flag", 2);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 2,
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
return "Success";
}catch (Exception e){
e.printStackTrace();
return "fail";
}
}
}
InstallSessionCallback
package com.project.readdata.utils;
import android.content.pm.PackageInstaller;
import android.util.Log;
class InstallSessionCallback extends PackageInstaller.SessionCallback {
private int mSessionId = -1;
private static final String TAG = "carden"+InstallSessionCallback.class.getSimpleName();
@Override
public void onCreated(int sessionId) {
// empty
Log.d(TAG, "onCreated()" + sessionId);
}
@Override
public void onBadgingChanged(int sessionId) {
// empty
Log.d(TAG, "onBadgingChanged()" + sessionId + "active");
}
@Override
public void onActiveChanged(int sessionId, boolean active) {
// empty
Log.d(TAG, "onActiveChanged()" + sessionId + "active" + active);
}
@Override
public void onProgressChanged(int sessionId, float progress) {
Log.d(TAG, "onProgressChanged()" + sessionId);
if (sessionId == mSessionId) {
int progres = (int) (Integer.MAX_VALUE * progress);
Log.d(TAG, "onProgressChanged" + progres);
}
}
@Override
public void onFinished(int sessionId, boolean success) {
// empty, finish is handled by InstallResultReceiver
Log.d(TAG, "onFinished()" + sessionId + "success" + success);
if (mSessionId == sessionId) {
if (success) {
Log.d(TAG, "onFinished() 安装成功");
} else {
Log.d(TAG, "onFinished() 安装失败");
}
}
}
}
InstallResultReceiver
package com.project.readdata.utils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.util.Log;
public class InstallResultReceiver extends BroadcastReceiver {
public final String TAG = "carden" + InstallResultReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "收到安装反馈广播了");
if (intent != null) {
final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
Log.d(TAG, "收到安装反馈广播了--"+status);
if (status == PackageInstaller.STATUS_SUCCESS) {
// success
int requestCode=intent.getIntExtra("flag",0);
if (requestCode==1){
Log.d(TAG,"静默安装成功");
}else if (requestCode==2){
Log.d(TAG,"静默卸载成功");
}
Log.d(TAG, "APP Install Success!");
} else {
String msg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
Log.e(TAG, "Install FAILURE status_massage" + msg);
}
}
}
}
mainfest:
<receiver
android:name=".utils.InstallResultReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.content.pm.extra.STATUS"/>
</intent-filter>
</receiver>
ok,就到这了