Android 無需root實現apk的靜默安裝
轉載請註明出處:http://blog.csdn.net/yyh352091626/article/details/50533137
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安裝到手機上,開啟,點選靜默安裝,在去程式頁看看,發現安裝成功~~
測試的時候發現一個問題,就是這樣的方法生成的APK只有在Android原生系統或者是自己編譯的系統中才可以用,因為這樣的系統才可以拿到platform.pk8 和platform.x509.pem這兩個檔案。比如在華為或小米的系統上,就無法安裝了。用原始的Android中的key 來簽名,程式在模擬器上執行OK(可能會出現重啟的情況),不過放到G3手機上安裝直接提示"Package ... has no signatures that match those in shared user android.uid.system",第三方Rom這樣做也是處於保護系統安全的角度來考慮。
本文主要是提供了一種實現靜默安裝的思路,但是具體怎麼做到相容各個系統,舉一反三,還需要各位看官去閱讀Android系統原始碼。
另外,由於被牆的緣故,很多人都無法下載系統原始碼,這裡再順便附上Android5.0系統原始碼的雲盤地址:連結: http://pan.baidu.com/s/1dElkPlZ 密碼: 4pue
最後,附上Demo的下載地址:Android 無需root實現APK靜默安裝 原始碼
相關文章
- android靜默安裝apk已經成功實現AndroidAPK
- android apk靜默安裝和解除安裝AndroidAPK
- android 4.0.4系統下實現apk的靜默安裝和啟動AndroidAPK
- Android靜默安裝和靜默解除安裝Android
- 靜默安裝功能的實現
- Android靜默安裝應用和靜默解除安裝應用Android
- Android 流氓軟體靜默安裝是怎麼實現的?Android
- Android 靜默安裝/後臺安裝Android
- 無需 root 實現在 Android 裝置上執行 LinuxAndroidLinux
- 應用黑名單以及靜默安裝的實現
- 如何從手機中提取apk,無需rootAPK
- rac靜默安裝
- dbca 靜默安裝
- Oracle 靜默安裝Oracle
- Oracle靜默安裝Oracle
- oracle客戶端無人值守安裝 靜默安裝Oracle客戶端
- PackageInstaller 5.0原始碼分析靜默安裝與靜默解除安裝Package原始碼
- 靜默安裝Azure CLI
- 靜默安裝ORACLE(文件)Oracle
- Android程式碼實現APK檔案的安裝與解除安裝AndroidAPK
- Oracle資料庫靜默安裝實踐Oracle資料庫
- oracle靜默安裝raw裝置Oracle
- 靜默安裝oracle時報錯Oracle
- ORACLE 11.2.0.4靜默安裝Oracle
- 靜默安裝oracle軟體Oracle
- Oracle靜默安裝(單機)Oracle
- 靜默安裝、建庫(轉)
- 靜默安裝ORACLE 軟體Oracle
- Oracle靜默安裝說明Oracle
- 【11g 單庫解除安裝、靜默安裝】實驗
- 【靜默】在RHEL 6.5上靜默安裝Oracle 18cOracle
- 繞過身份檢測,破解Android SU(android靜默安裝)Android
- oracle 19C 靜默安裝Oracle
- Linux下靜默安裝OraceLinux
- 靜默安裝and手動建庫
- oracle 12c 靜默安裝Oracle
- Oracle 11g 靜默安裝Oracle
- 靜默安裝oracle10gOracle