Android无需root实现apk的静默安装_Android

Android的静默安装似乎是一个很有趣很诱人的东西,但是,用普通做法,如果手机没有root权限的话,似乎很难实现静默安装,因为Android并不提供显示的Intent调用,一般是通过以下方式安装apk:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);

但是,这并没有真正的实现静默安装,因为有用户界面,会让用户知道。那么,怎么在后台悄悄的安装APK呢?只能试图去看看Android系统源码正常安装APK的过程,我这边下载的源码是Android5.0系统的,5个G的大小,但是可能由于Android5.0有一些安全方面的更新,跟之前的版本还是有一定的差距的,但是,学会一个之后再去学另一个相似的过程,那就简单许多了,就像学会了C语言,再学Java,也并非什么难事。

Android系统把所有的Permission(权限)依据其潜在风险划分为四个等级,即"normal"、 "dangerous"、 "signature"、 "signatureOrSystem"。APK的安装对应的权限是 INSTALL_PACKAGES,权限等级属于后两者。所以,最终想实现APK的静默安装,必然需要一些特殊的处理,执行安装的这个进程,须为系统进程。
那么,我们就来看看Android自身是如何实现安装APK的。安装的命令是pm install... 我们定位到系统源码的/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java这个文件,他实现了pm命令,我们看runInstall方法,这就是APK的安装过程。

private void runInstall() {
  int installFlags = 0;
  int userId = UserHandle.USER_ALL;
  String installerPackageName = null; 

  String opt; 

  String originatingUriString = null;
  String referrer = null;
  String abi = null; 

  while ((opt=nextOption()) != null) {
    if (opt.equals("-l")) {
      installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
    } else if (opt.equals("-r")) {
      installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
    } else if (opt.equals("-i")) {
      installerPackageName = nextOptionData();
      if (installerPackageName == null) {
        System.err.println("Error: no value specified for -i");
        return;
      }
    } else if (opt.equals("-t")) {
      installFlags |= PackageManager.INSTALL_ALLOW_TEST;
    } else if (opt.equals("-s")) {
      // Override if -s option is specified.
      installFlags |= PackageManager.INSTALL_EXTERNAL;
    } else if (opt.equals("-f")) {
      // Override if -s option is specified.
      installFlags |= PackageManager.INSTALL_INTERNAL;
    } else if (opt.equals("-d")) {
      installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
    } else if (opt.equals("--originating-uri")) {
      originatingUriString = nextOptionData();
      if (originatingUriString == null) {
        System.err.println("Error: must supply argument for --originating-uri");
        return;
      }
    } else if (opt.equals("--referrer")) {
      referrer = nextOptionData();
      if (referrer == null) {
        System.err.println("Error: must supply argument for --referrer");
        return;
      }
    } else if (opt.equals("--abi")) {
      abi = checkAbiArgument(nextOptionData());
    } else if (opt.equals("--user")) {
      userId = Integer.parseInt(nextOptionData());
    } else {
      System.err.println("Error: Unknown option: " + opt);
      return;
    }
  } 

  if (userId == UserHandle.USER_ALL) {
    userId = UserHandle.USER_OWNER;
    installFlags |= PackageManager.INSTALL_ALL_USERS;
  } 

  final Uri verificationURI;
  final Uri originatingURI;
  final Uri referrerURI; 

  if (originatingUriString != null) {
    originatingURI = Uri.parse(originatingUriString);
  } else {
    originatingURI = null;
  } 

  if (referrer != null) {
    referrerURI = Uri.parse(referrer);
  } else {
    referrerURI = null;
  } 

  // Populate apkURI, must be present
  final String apkFilePath = nextArg();
  System.err.println("\tpkg: " + apkFilePath);
  if (apkFilePath == null) {
    System.err.println("Error: no package specified");
    return;
  } 

  // Populate verificationURI, optionally present
  final String verificationFilePath = nextArg();
  if (verificationFilePath != null) {
    System.err.println("\tver: " + verificationFilePath);
    verificationURI = Uri.fromFile(new File(verificationFilePath));
  } else {
    verificationURI = null;
  } 

  LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
  try {
    VerificationParams verificationParams = new VerificationParams(verificationURI,
        originatingURI, referrerURI, VerificationParams.NO_UID, null); 

    mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
        installerPackageName, verificationParams, abi, userId); //注意!!最终就是调用这个方法来进行安装的 

    synchronized (obs) {
      while (!obs.finished) {
        try {
          obs.wait();
        } catch (InterruptedException e) {
        }
      }
      if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
        System.out.println("Success");
      } else {
        System.err.println("Failure ["
            + installFailureToString(obs)
            + "]");
      }
    }
  } catch (RemoteException e) {
    System.err.println(e.toString());
    System.err.println(PM_NOT_RUNNING_ERR);
  }
}

知道了这个过程之后,就大概知道怎么做了。既然系统底层把这个API屏蔽了,那就想办法去绕过这层屏蔽,来使用它。首先想到的就是使用AIDL,不知道AIDL这东西的,先问度娘去吧~~在上面的代码中,最终实现安装的那一句话,mPm.installPackageAsUser(...),mPm是个什么东西?不难发现,IPackageManager类型,那么这个类从哪里来?搜寻一下,位于/frameworks/base/core/java/android/content/pm这个包底下,拷贝到我们工程目录底下,包名不能变,只拷贝这一个文件的话,一定是不行了,会报其他的一些aidl找不到,相应地也拷贝过来。Android5.0中,aidl改动还是比较大的,所以要拷贝很多东西过来,还要进行一些改动...我也是花了挺久才改到他没报错。
最终,工程的目录如下所示~~

那么,如何来使用它呢?

  • 1、先获取系统服务android.os.ServiceManager,这个又是隐藏的,怎么办?考验Java水平的时候到了~~没错,用反射机制,来获取ServiceManager类,以及该类里面的方法;
  • 2、有了服务之后,我们就要去拿到IPackageManager这个对象;
  • 3、调用IPackageManager里面的installPackage方法进行安装;

实现代码如下:

package com.example.autoinstall; 

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method; 

import android.app.Activity;
import android.content.Intent;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageManager;
import android.content.pm.VerificationParams;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View; 

public class MainActivity extends Activity { 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  } 

  /**
   * Button点击事件
   * @param view
   */
  public void install(View view)
  {
    String path = "";
    if (FileUtils.isSdcardReady()) {
      path = FileUtils.getSdcardPath();
    } else {
      path = FileUtils.getCachePath(this);
    }
    String fileName = path + "/AidlServerDemo.apk";
    File file = new File(fileName); 

    try {
      if(!file.exists())
        copyAPK2SD(fileName);
      Uri uri = Uri.fromFile(new File(fileName));
            // 通过Java反射机制获取android.os.ServiceManager
      Class<?> clazz = Class.forName("android.os.ServiceManager");
      Method method = clazz.getMethod("getService", String.class);
      IBinder iBinder = (IBinder) method.invoke(null, "package");
      IPackageManager ipm = IPackageManager.Stub.asInterface(iBinder);
      @SuppressWarnings("deprecation")
      VerificationParams verificationParams = new VerificationParams(null, null, null, VerificationParams.NO_UID, null);
            // 执行安装(方法及详细参数,可能因不同系统而异)
      ipm.installPackage(fileName, new PackageInstallObserver(), 2, null, verificationParams, "");
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } 

  } 

  // 用于显示结果
  class PackageInstallObserver extends IPackageInstallObserver2.Stub { 

    @Override
    public void onUserActionRequired(Intent intent) throws RemoteException {
      // TODO Auto-generated method stub 

    } 

    @Override
    public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) throws RemoteException {
      //returnCode<span style="font-family: Arial, Helvetica, sans-serif;">为1,就是安装成功</span> 

    }
  }; 

  /**
   * 拷贝assets文件夹的APK插件到SD
   *
   * @param strOutFileName
   * @throws IOException
   */
  private void copyAPK2SD(String strOutFileName) throws IOException {
    FileUtils.createDipPath(strOutFileName);
    InputStream myInput = this.getAssets().open("AidlServerDemo.apk");
    OutputStream myOutput = new FileOutputStream(strOutFileName);
    byte[] buffer = new byte[1024];
    int length = myInput.read(buffer);
    while (length > 0) {
      myOutput.write(buffer, 0, length);
      length = myInput.read(buffer);
    }
    myOutput.flush();
    myInput.close();
    myOutput.close();
  }
}

每个版本的系统源码里面的aidl可能会不一样,所以具体调用的方法和参数,还得根据实际情况而定,需要去仔细阅读Pm.java这个文件的源码。
在其他版本可能只需要拷贝这4个文件:PackageManager.java、 IPackageDeleteObserver.aidl 、IPackagerInstallObserver.aidl、 IPackageMoveObserver.aidl
然后,还需在配置清单文件里面添加INSTALL_PACKAGE权限

<uses-permission android:name="android.permission.INSTALL_PACKAGES"/> 

然后把该应用的uid设置为系统级别的,在manifest标签下添加以下属性

android:sharedUserId="android.uid.system" 

仅仅这样的话,还是没法实现静默安装,因为系统并不认为你这个app是系统级别的应用,所以,还应该对该应用的APK进行系统签名(注意:不是那个静默安装的APK,是这个实现静默安装程序的APK)。签名过程如下:
总共需要三个文件:

  • 1、SignApk.jar                      %系统源码%/out/host/linux-x86/framework/signapk.jar
  • 2、platform.x509.pem          %系统源码%/build/target/product/security/platform.x509.pem
  • 3、platform.pk8                    %系统源码%/build/target/product/security/platform.pk8

打开终端,执行命令 java -jar SignApk.jar platform.x509.pem platform.pk8 未签名APK 签名后APK,例如
java -jar SignApk.jar platform.x509.pem  platform.pk8 AutoInstall.apk AutoInstall_new.apk 

之后,把签名过后的APK安装到手机上,打开,点击静默安装,在去程序页看看,发现安装成功~~

      

 

本文主要是提供了一种实现静默安装的思路,但是具体怎么做到兼容各个系统,举一反三,

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
, apk
, root
静默安装
无需root实现静默安装、非root静默安装apk、不root实现静默功能、android静默安装apk、apk静默安装,以便于您获取更多的相关知识。

时间: 2025-01-15 11:34:18

Android无需root实现apk的静默安装_Android的相关文章

Android无需root实现apk的静默安装

Android的静默安装似乎是一个很有趣很诱人的东西,但是,用普通做法,如果手机没有root权限的话,似乎很难实现静默安装,因为Android并不提供显示的Intent调用,一般是通过以下方式安装apk: Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); startAct

Android简单判断某个APK是否已经安装的方法_Android

本文实例讲述了Android简单判断某个APK是否已经安装的方法.分享给大家供大家参考,具体如下: privateboolean isAppInstalled(String uri){ PackageManager pm = getPackageManager(); boolean installed =false; try{ pm.getPackageInfo(uri,PackageManager.GET_ACTIVITIES); installed =true; }catch(Package

Android简单判断某个APK是否已经安装的方法

本文实例讲述了Android简单判断某个APK是否已经安装的方法.分享给大家供大家参考,具体如下: privateboolean isAppInstalled(String uri){ PackageManager pm = getPackageManager(); boolean installed =false; try{ pm.getPackageInfo(uri,PackageManager.GET_ACTIVITIES); installed =true; }catch(Package

Android程序版本更新之通知栏更新下载安装_Android

Android应用检查版本更新后,在通知栏下载,更新下载进度,下载完成自动安装,效果图如下: •检查当前版本号 AndroidManifest文件中的versionCode用来标识版本,在服务器放一个新版本的apk,versioncode大于当前版本,下面代码用来获取versioncode的值 PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); int

Android中Root权限获取的简单代码_Android

我们知道Android手机操作系统采用的是Linux内核,Linux中最高的系统权限就是Root,这就类似与Windows中的Administrator系统管理员权限,也就是操作系统的最高权限.由于Root的权限过高,因此手机产商一般都不允许用户直接拥用Root权限,以防止用户修改系统内置的业务跟程序.但是对于用户来说,当然希望能拥有Root权限以将手机修改成自己的一种特色,因为有Root权限则可以任意修改手机的所有文件跟程序,让手机更加个性化. 复制代码 代码如下: Process proce

android 静默安装-android 定制系统下的静默安装

问题描述 android 定制系统下的静默安装 目前的要求是在ACER-Z120手机上(该手机应该是基于android 4.1.1的定制系统)实现软件的静默安装.就是我要写一个控制软件来下载被控制软件,下载完了静默安装被控制软件.我的控制软件要执行 pm install 命令是不是要先得到该操作系统的platform..pk8 platform.x509.pem signapk.jar三个文件,用命令来对我的控制软件进行签名,在安装进入该手机?整个过程这样对吗? 解决方案 放在/system/a

Android静默安装实现方案 仿360手机助手秒装和智能安装功能_Android

之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢?所谓的静默安装,就是不用弹出系统的安装界面,在不影响用户任何操作的情况下不知不觉地将程序装好.虽说这种方式看上去不打搅用户,但是却存在着一个问题,因为Android系统会在安装界面当中把程序所声明的权限展示给用户看,用户来评估一下这些权限然后决定是否要安装该程序,但如果使用了静默安装的方式,也就没有地方让用户看权限了,相当于用户被动接受了这些权限.在Android官方看来,这显示是一种非常危险的行为,因此静默安装这一行为系统是

仿360静默安装

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47803149 之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢?所谓的静默安装,就是不用弹出系统的安装界面,在不影响用户任何操作的情况下不知不觉地将程序装好.虽说这种方式看上去不打搅用户,但是却存在着一个问题,因为Android系统会在安装界面当中把程序所声明的权限展示给用户看,用户来评估一下这些权限然后决定是否要安装该程序,但如果使用了静默安装的方式,也就没

Android权限机制带来的一些安全问题介绍_Android

Android引入了权限机制最初的出发点是为了通过权限策略来严格控制和处理安全问题,可参见:下面两篇文章,但关于这个Android权限的机制仍然存在一些很小但不容忽略的问题,另外正所谓道高一尺魔高一丈,仍然存在一些可以绕过权限的方法.本文旨在分析权限机制的一些缺点和不足,并不能以此文章作为非法应用的参考书. Android Permission权限机制引子 Android Permission权限机制的具体使用 权限机制的缺陷和不足 (1) 应用程序可以自由地命名一个新的权限,无须遵循一定的命名