先看效果:

android 测试静默安装 android 静默卸载_软件安装


静默安装的思路就是一个,就是用 adb install -r (apk路径) 或者 pm install -r (apk路径),用这种方式安装apk,是不会提示用户安装界面,所以,我们的思路就是在代码实现上诉的命令即可。

不过, 上诉命令需要 root 权限,然而,很多手机在出厂的时候,是做了定制的,即很多 root 是不开放的;而这个时候,我们就需要做判断了。

//获取超级权限
  Process process = Runtime.getRuntime().exec("su");
  dataOutputStream = new DataOutputStream(process.getOutputStream());
  dataOutputStream.flush();
  dataOutputStream.close();
  int value = process.waitFor();

如果你在测试上面代码的时候,发现可以弹出以下的图片的时候,则表示你的手机是可以被root的,即我们可以以第三方应用的方式,实现静默安装。

android 测试静默安装 android 静默卸载_android_02

但大部分时候,我们用上面的命令去测试的时候,会提示 “su: uid 10032 not allowed to su” 这样的错误

为什么会这样呢,首先,我们从源码上看, /system/extras/su.c 中有这样一段话:

if (myuid != AID_ROOT && myuid != AID_SHELL) {
        fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
        return 1;
}

可以看到,只有 uid 为 root 或者 shell 的时候,才会有权限去执行我们的 su 指令。

那么,这里就有个办法了,就是把上面那段话屏蔽掉;然后用 mmm 编译的方式,把新生成的 su 替换掉原先的 /system/xbin/su 就可以了。
然后,如果这时,有点平台就可以了,有点平台则继续会提示 我们这个进程没有权限。那怎么办?
别急,我们只要将上面的 “su” 换成 “sh” ,然后把该命令换成 系统应用就可以了。所以,我们测试平台的代码应该如下:

/**
     * 判断系统是否拥有su权限或者sh权限
     * @return
     */
    private boolean hasRootRight(){
        if (new File("/system/xbin/su").exists() || new File("/system/bin/su").exists()){
            DataOutputStream dataOutputStream = null;
            try {
                //获取超级权限
                Process process = Runtime.getRuntime().exec("su");
                dataOutputStream = new DataOutputStream(process.getOutputStream());
                dataOutputStream.flush();
                dataOutputStream.close();
                int value = process.waitFor();
                if (value != 0) {// 0 表示拥有 su 权限,如果没有 su 权限
                    //获取 sh 权限,注意,如果没有su权限,则用sh的时候,则需要系统应用才可行,即在 /system/app下
                    process = Runtime.getRuntime().exec("sh");
                    dataOutputStream = new DataOutputStream(process.getOutputStream());
                    dataOutputStream.flush();
                    dataOutputStream.close();
                    value = process.waitFor();
                }
                return returnResult(value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

然后,我们的安装代码如下:

/**
     *
     * @param apkPath
     * @return
     */
    public void install(final String apkPath, final InstallListener listener){
        if (hasRootRight()){  //有权限则静默安装
            new Thread(new Runnable() {  //安装时耗时的,需要用线程
                @Override
                public void run() {
                    slientInstall(apkPath,listener);
                }
            }).start();
        }else{  //没有权限则普通模式安装
            File file = new File(apkPath);
            if (!file.exists()){
                return ;
            }
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
            mContext.startActivity(intent);
        }
    }

其中 slientInstall :

public void slientInstall(String apkPath,final InstallListener listener){
        if (TextUtils.isEmpty(apkPath)){
            listener.fail("请选择安装包");
            return;
        }
        DataOutputStream dataOutputStream = null;  //将Java基本数据类型写入数据输出流中
        BufferedReader br = null;
        try {
            //获取超级权限,如果没有su权限,则用sh,但要把应用改成系统应用
            Process process = Runtime.getRuntime().exec("su");
            dataOutputStream = new DataOutputStream(process.getOutputStream());
            //执行 pm slientInstall -r (apk路径)指令
            String common = "pm install -r "+apkPath+"\n";
            dataOutputStream.write(common.getBytes(Charset.forName("utf-8"))); //保持统一,用utf-8类型
            dataOutputStream.flush();
            dataOutputStream.writeBytes("exit\n");
            dataOutputStream.flush();
            process.waitFor();  //等待安装完毕
            //获取失败的参数
            br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String msg = null;
            String line;
            while((line = br.readLine()) != null ){
                msg += line;
            }
            Log.d("zsr", "slientInstall: "+msg);
            if (msg.contains("Failure") || msg.contains("su")||msg.contains("kill")){
                final String finalMsg = msg;
                mHandler.post(new Runnable() {  //这里用 ui线程,这样,就可以在 callback更新UI 了
                    @Override
                    public void run() {
                        listener.fail(finalMsg);
                    }
                });
            } else{
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        listener.success();
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (dataOutputStream != null){
                    dataOutputStream.close();
                }
                if (br != null){
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

主函数的安装函数如下:

public void install(View view){
              //  Toast.makeText(this, "有root权限", Toast.LENGTH_SHORT).show();
            mProgressBar.setVisibility(View.VISIBLE);
            mSlientInstall.install(apkPath, new InstallListener() {
                @Override
                public void success() {
                    mProgressBar.setVisibility(View.GONE);
                    Log.d(TAG, "install success ");
                    Toast.makeText(MainActivity.this, "安装成功", Toast.LENGTH_SHORT).show();
                }
                @Override
                public void fail(String errormsg) {
                    Log.d(TAG, "fail: ");
                    mProgressBar.setVisibility(View.GONE);
                    Toast.makeText(MainActivity.this, "安装失败: "+errormsg, Toast.LENGTH_SHORT).show();
                }
            });
     }

别忘记了再 androidmanifest.xml 加上:

android:sharedUserId="android.uid.system"

然后,用 adb push “apk路径” /system/app 下就可以了,如果提示没权限,你则需要用adb chmod 777 改变system和app的权限了。