会挽雕弓如满月,西北望,射天狼。这篇文章主要讲述Android的静默安装相关的知识,希望能为你提供帮助。
原文
【Android的静默安装】
android的静默安装似乎是一个很有趣很诱人的东西,但是,用普通做法,如果手机没有root权限的话,似乎很难实现静默安装,因为Android并不提供显示的Intent调用,一般是通过以下方式安装apk:
?
1 2 3 | Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive" );
startActivity(intent);
|
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的安装过程。
?
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);
//注意!!最终就是调用这个方法来进行安装的
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);
}
} |
最终,工程的目录如下所示~~
文章图片
那么,如何来使用它呢?
- 1、先获取系统服务android.os.ServiceManager,这个又是隐藏的,怎么办?考验Java水平的时候到了~~没错,用反射机制,来获取ServiceManager类,以及该类里面的方法;
- 2、有了服务之后,我们就要去拿到IPackageManager这个对象;
- 3、调用IPackageManager里面的installPackage方法进行安装;
?
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点击事件
* @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();
}
} |
在其他版本可能只需要拷贝这4个文件:PackageManager.java、 IPackageDeleteObserver.aidl 、IPackagerInstallObserver.aidl、 IPackageMoveObserver.aidl
然后,还需在配置清单文件里面添加INSTALL_PACKAGE权限
?
1 | <
uses-permission android:name= "android.permission.INSTALL_PACKAGES" />
|
?
1 | android:sharedUserId= "android.uid.system" |
总共需要三个文件:
- 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 AutoInstall.apk AutoInstall_new.apk
之后,把签名过后的APK安装到手机上,打开,点击静默安装,在去程序页看看,发现安装成功~~
文章图片
文章图片
本文主要是提供了一种实现静默安装的思路,但是具体怎么做到兼容各个系统,举一反三,
推荐阅读
- VS2017 Cordova 出现错误 @ionic/app-scripts 未安装
- 我是如何理解Android的Handler模型_2
- 出现Android.os.NetworkOnMainThreadException 错误
- Android studio统计项目总行数
- Android基础新手教程——4.2.3 Service精通
- android 中ProgressBar的使用
- androiduses-permission和permission具体解释
- IDEA:Application Server was not connected before run configuration stop, reason: Unable to ping 1099
- Android 广播接受者