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

2019-12-10 19:14:28于海丽

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

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

那么,如何来使用它呢?

  • 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(); 
      } 
    }