当前位置: 首页 > 编程笔记 >

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

韩寒
2023-03-14
本文向大家介绍Android无需root实现apk的静默安装,包括了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"); 
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安装配置教程》进行学习。

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

 类似资料:
  • 本文向大家介绍java实现静默安装apk,包括了java实现静默安装apk的使用技巧和注意事项,需要的朋友参考一下 静默安装就是偷偷的把一个应用安装到手机上,就是屏蔽确认框,通过反射 只能写个主要的代码,这个是在linux编译用到,因为静默安装需要调用系统服务,在源码下编译,我也是搞了好久 InstallActivity.java main.xml 以上所述就是本文的全部内容了,希望大家能够喜欢。

  • 本文向大家介绍Android实现静默安装实例代码,包括了Android实现静默安装实例代码的使用技巧和注意事项,需要的朋友参考一下 静默安装主要分为以下几种方式: 一、在ROOT过的机器上,在App中使用pm install指令安装APK: 二、修改系统应用PackageManagerIntaller.apk的源码,增加无界面的安装接口: 原理 在Android的文件夹中点击一个APK时,触发安装

  • 本文向大家介绍Android实现静默安装的两种方法,包括了Android实现静默安装的两种方法的使用技巧和注意事项,需要的朋友参考一下 前言 一般情况下,Android系统安装apk会出现一个安装界面,用户可以点击确定或者取消来进行apk的安装。 但在实际的项目需求中,有一种需求,就是希望apk在后台安装(不出现安装界面的提示),这种安装方式称为静默安装。下面这篇文章就给大家介绍了两种方法来实现,

  • 本文向大家介绍android实现静默安装与卸载的方法,包括了android实现静默安装与卸载的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了android实现静默安装与卸载的方法。分享给大家供大家参考。具体如下: 方法1:【使用调用接口方法,由于安装卸载应用程序的部分API是隐藏的,所以必须下载Android系统源码,在源码下开发并编译之后使用MM命令编译生成APK文件】 安装、卸载

  • 本文向大家介绍android 无须root截图方案的实现,包括了android 无须root截图方案的实现的使用技巧和注意事项,需要的朋友参考一下 通过反射了截取屏 这是我们反射调用SurfaceControl.screenshot()和Surface.screenshot(),他们都是系统提供的截屏方法,可是这个方法被@hide的了,无法调用,我们可是使用反射的方式调用,可是我们普通用户通过代码

  • 我的应用程序安装良好,当安装通过USB“运行应用程序”按钮在Android Studio。 但是,当我构建一个签名APK并在卸载旧APK后将其安装到我的设备上时,n它不起作用:“Application not installed”。 祝你有愉快的一天

  • 本文向大家介绍Android 8.0安装apk的实例代码,包括了Android 8.0安装apk的实例代码的使用技巧和注意事项,需要的朋友参考一下 android 8.0安装apk需要请求未知来源权限 总结 以上所述是小编给大家介绍的Android 8.0安装apk的实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对呐喊教程网站的支持!

  • 我创建了一个基本的Android应用程序作为实习。创建了它的APK并尝试通过USB电缆将其连接到笔记本电脑并用作模拟器在我的手机上运行,它运行良好。我的Android操作系统版本是Oreo,我尝试在Lollypop上做同样的事情,但它仍然在执行。 当我通过电子邮件发送APK并尝试直接安装在设备上时,问题就来了,它给了我这个错误 解析包时出现问题 我不知道发生了什么,因为它是从Android Stu