一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Android - Android無(wú)需root實(shí)現(xiàn)apk的靜默安裝

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

2021-05-14 15:56初頁(yè) Android

這篇文章主要介紹了Android無(wú)需root實(shí)現(xiàn)apk的靜默安裝 的相關(guān)資料,需要的朋友可以參考下

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

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

但是,這并沒有真正的實(shí)現(xiàn)靜默安裝,因?yàn)橛杏脩艚缑妫瑫?huì)讓用戶知道。那么,怎么在后臺(tái)悄悄的安裝APK呢?只能試圖去看看Android系統(tǒng)源碼正常安裝APK的過程,我這邊下載的源碼是Android5.0系統(tǒng)的,5個(gè)G的大小,但是可能由于Android5.0有一些安全方面的更新,跟之前的版本還是有一定的差距的,但是,學(xué)會(huì)一個(gè)之后再去學(xué)另一個(gè)相似的過程,那就簡(jiǎn)單許多了,就像學(xué)會(huì)了C語(yǔ)言,再學(xué)Java,也并非什么難事。

Android系統(tǒng)把所有的Permission(權(quán)限)依據(jù)其潛在風(fēng)險(xiǎn)劃分為四個(gè)等級(jí),即"normal"、 "dangerous"、 "signature"、 "signatureOrSystem"。APK的安裝對(duì)應(yīng)的權(quán)限是 INSTALL_PACKAGES,權(quán)限等級(jí)屬于后兩者。所以,最終想實(shí)現(xiàn)APK的靜默安裝,必然需要一些特殊的處理,執(zhí)行安裝的這個(gè)進(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這個(gè)文件,他實(shí)現(xiàn)了pm命令,我們看runInstall方法,這就是APK的安裝過程。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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)用這個(gè)方法來進(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);
 }
}

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

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

那么,如何來使用它呢?

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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();
 }
}

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

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

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

?
1
android:sharedUserId="android.uid.system"

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

  • 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)擊靜默安裝,在去程序頁(yè)看看,發(fā)現(xiàn)安裝成功~~

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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 無码一区中文字幕少妇熟女H | 公园吃女人奶野战视频 | 日韩欧美高清 | 变态 另类 国产 亚洲 | 吻戏辣妞范1000免费体验 | 亚洲精品一区二区三区在线看 | 精品国产欧美一区二区 | 亚洲成人77777 | 星空无限传媒xk8046 | 99热6这里只有精品 99欧美精品 | 激情视频激情小说 | 亚洲欧美成人中文在线网站 | 日韩毛片在线影视 | 欧美性色老妇人 | 热辣小秘书办公室 | 手机亚洲第一页 | 亚洲免费在线观看 | www.色婷婷.com | 久久国产精品人妻中文 | 国产免费又粗又猛又爽视频国产 | 香蕉国产人午夜视频在线观看 | 国产卡一卡二卡三乱码手机 | 236zz宅宅最新伦理 | 国产精品嫩草影院一二三区入口 | 污污免费 | 特黄视频免费看 | 成人网址大全 | 国产老熟 | 亚洲精品一线二线三线 | 久久精品国产亚洲AV天美18 | 荡女淫春2未删减版 | 国产精品自产拍在线观看2019 | 亚洲精品久久久WWW游戏好玩 | 日产精品一二三四区国产 | 扒开尿口 | 大陆男男gayxxxxvideo | 色婷婷影院在线视频免费播放 | 亚洲视频在线免费观看 | 韩日理论片 | 国产一级持黄大片99久久 | 日本动漫黄网站在线观看 |