国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android無需root實(shí)現(xiàn)apk的靜默安裝

2020-04-11 10:55:34
字體:
供稿:網(wǎng)友

Android的靜默安裝似乎是一個很有趣很誘人的東西,但是,用普通做法,如果手機(jī)沒有root權(quán)限的話,似乎很難實(shí)現(xiàn)靜默安裝,因?yàn)锳ndroid并不提供顯示的Intent調(diào)用,一般是通過以下方式安裝apk:

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

但是,這并沒有真正的實(shí)現(xiàn)靜默安裝,因?yàn)橛杏脩艚缑妫瑫層脩糁馈D敲矗趺丛诤笈_悄悄的安裝APK呢?只能試圖去看看Android系統(tǒng)源碼正常安裝APK的過程,我這邊下載的源碼是Android5.0系統(tǒng)的,5個G的大小,但是可能由于Android5.0有一些安全方面的更新,跟之前的版本還是有一定的差距的,但是,學(xué)會一個之后再去學(xué)另一個相似的過程,那就簡單許多了,就像學(xué)會了C語言,再學(xué)Java,也并非什么難事。

Android系統(tǒng)把所有的Permission(權(quán)限)依據(jù)其潛在風(fēng)險劃分為四個等級,即"normal"、 "dangerous"、 "signature"、 "signatureOrSystem"。APK的安裝對應(yīng)的權(quán)限是 INSTALL_PACKAGES,權(quán)限等級屬于后兩者。所以,最終想實(shí)現(xiàn)APK的靜默安裝,必然需要一些特殊的處理,執(zhí)行安裝的這個進(jìn)程,須為系統(tǒng)進(jìn)程。
那么,我們就來看看Android自身是如何實(shí)現(xiàn)安裝APK的。安裝的命令是pm install... 我們定位到系統(tǒng)源碼的/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java這個文件,他實(shí)現(xiàn)了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); //注意!!最終就是調(diào)用這個方法來進(jìn)行安裝的    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);  } } 

知道了這個過程之后,就大概知道怎么做了。既然系統(tǒng)底層把這個API屏蔽了,那就想辦法去繞過這層屏蔽,來使用它。首先想到的就是使用AIDL,不知道AIDL這東西的,先問度娘去吧~~在上面的代碼中,最終實(shí)現(xiàn)安裝的那一句話,mPm.installPackageAsUser(...),mPm是個什么東西?不難發(fā)現(xiàn),IPackageManager類型,那么這個類從哪里來?搜尋一下,位于/frameworks/base/core/java/android/content/pm這個包底下,拷貝到我們工程目錄底下,包名不能變,只拷貝這一個文件的話,一定是不行了,會報其他的一些aidl找不到,相應(yīng)地也拷貝過來。Android5.0中,aidl改動還是比較大的,所以要拷貝很多東西過來,還要進(jìn)行一些改動...我也是花了挺久才改到他沒報錯。
最終,工程的目錄如下所示~~

那么,如何來使用它呢?

  • 1、先獲取系統(tǒng)服務(wù)android.os.ServiceManager,這個又是隱藏的,怎么辦?考驗(yàn)Java水平的時候到了~~沒錯,用反射機(jī)制,來獲取ServiceManager類,以及該類里面的方法;
  • 2、有了服務(wù)之后,我們就要去拿到IPackageManager這個對象;
  • 3、調(diào)用IPackageManager里面的installPackage方法進(jìn)行安裝;

實(shí)現(xiàn)代碼如下:

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點(diǎn)擊事件   * @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反射機(jī)制獲取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);       // 執(zhí)行安裝(方法及詳細(xì)參數(shù),可能因不同系統(tǒng)而異)    ipm.installPackage(fileName, new PackageInstallObserver(), 2, null, verificationParams, "");   } catch (Exception e) {    // TODO Auto-generated catch block    e.printStackTrace();   }   }   // 用于顯示結(jié)果  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();  } } 

每個版本的系統(tǒng)源碼里面的aidl可能會不一樣,所以具體調(diào)用的方法和參數(shù),還得根據(jù)實(shí)際情況而定,需要去仔細(xì)閱讀Pm.java這個文件的源碼。
在其他版本可能只需要拷貝這4個文件:PackageManager.java、 IPackageDeleteObserver.aidl 、IPackagerInstallObserver.aidl、 IPackageMoveObserver.aidl
然后,還需在配置清單文件里面添加INSTALL_PACKAGE權(quán)限

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

然后把該應(yīng)用的uid設(shè)置為系統(tǒng)級別的,在manifest標(biāo)簽下添加以下屬性

android:sharedUserId="android.uid.system" 

僅僅這樣的話,還是沒法實(shí)現(xiàn)靜默安裝,因?yàn)橄到y(tǒng)并不認(rèn)為你這個app是系統(tǒng)級別的應(yīng)用,所以,還應(yīng)該對該應(yīng)用的APK進(jìn)行系統(tǒng)簽名(注意:不是那個靜默安裝的APK,是這個實(shí)現(xiàn)靜默安裝程序的APK)。簽名過程如下:
總共需要三個文件:

  • 1、SignApk.jar                      %系統(tǒng)源碼%/out/host/linux-x86/framework/signapk.jar
  • 2、platform.x509.pem          %系統(tǒng)源碼%/build/target/product/security/platform.x509.pem
  • 3、platform.pk8                    %系統(tǒng)源碼%/build/target/product/security/platform.pk8

打開終端,執(zhí)行命令 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安裝到手機(jī)上,打開,點(diǎn)擊靜默安裝,在去程序頁看看,發(fā)現(xiàn)安裝成功~~

      

 

更多內(nèi)容可以參考專題《android安裝配置教程》進(jìn)行學(xué)習(xí)。

本文主要是提供了一種實(shí)現(xiàn)靜默安裝的思路,但是具體怎么做到兼容各個系統(tǒng),舉一反三,

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 英德市| SHOW| 周宁县| 西充县| 苗栗县| 乐至县| 易门县| 定结县| 延庆县| 宜城市| 邻水| 尤溪县| 宜昌市| 台南县| 孟津县| 沧源| 阳春市| 自治县| 册亨县| 西和县| 田林县| 三门县| 山阳县| 遂昌县| 平潭县| 中宁县| 永安市| 大同县| 宜宾县| 体育| 彭水| 兴安盟| 珲春市| 丹棱县| 绥芬河市| 墨竹工卡县| 清新县| 琼海市| 江达县| 左贡县| 宣恩县|