android5.0--9.0静默安装,亲测可用

本文介绍了在不同Android版本中实现静默安装和卸载应用程序的方法。针对6.0及以下版本,使用特定权限和ADB命令;对于7.0及以上版本,采用反射机制解决问题;在Android 9.0中,则利用PackageInstaller API进行静默安装。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

最近在做有关静默安装和静默卸载的功能,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,就到这了

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

carden_coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值