【此篇文章为转载文章】
最近需要实现Android应用的静默安装,在网上看了不少帖子,最后在root权限下实现对应用的静默安装和卸载,现在就整个实现的过程做一个总结。
一.第一种方案第一种方案参考了源码中/packages/apps/PackageInstaller的实现方式,实现的主要代码如下:
01
02
03
04
05
06
07
08
09
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
import
java.io.File; import
java.io.FileNotFoundException; import
java.io.FileOutputStream; import
java.io.IOException; import
android.content.Context; import
android.content.Intent; import
android.content.pm.PackageInfo; import
android.content.pm.PackageManager; import
android.content.pm.IPackageInstallObserver; import
android.content.pm.PackageManager.NameNotFoundException; import
android.content.pm.PackageParser; import
android.net.Uri; import
android.os.Handler; import
android.os.Message; import
android.util.DisplayMetrics; import
android.util.Log; import
android.os.FileUtils; public
class
MyPackageInstaller { private
static
final
String PACKAGE_NAME = "test.installservice" ; private
final
int
INSTALL_COMPLETE = 1 ; private
Context context; Uri
mPackageURI; private
PackageParser.Package mPkgInfo; private
Handler mHandler = new
Handler() { public
void
handleMessage(Message msg) { switch
(msg.what) { case
INSTALL_COMPLETE: //
finish the activity posting result //setResultAndFinish(msg.arg1); break ; default : break ; } } }; void
setResultAndFinish( int
retCode) { //
Intent data = new Intent(); //
setResult(retCode); //
finish(); } public
MyPackageInstaller(Context c) { this .context
= c; } public
void
installPackage() { int
installFlags = 0 ; PackageManager
pm = context.getPackageManager(); try
{ PackageInfo
pi = pm.getPackageInfo( mPkgInfo.applicationInfo.packageName, PackageManager.GET_UNINSTALLED_PACKAGES); if
(pi != null )
{ //
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; installFlags
|= 2 ; } }
catch
(NameNotFoundException e) { } String
array[] = null ; try
{ Runtime.getRuntime().exec( "chmod
777 /data/data/"
+ PACKAGE_NAME); Runtime.getRuntime().exec( "chmod
777 /data/data/"
+ PACKAGE_NAME + "/files" ); array
= this .mPackageURI.toString().split( "/" ); System.out.println( "array[last]->"
+ array[array.length - 1 ]); Runtime.getRuntime().exec( "chmod
777 /data/data/"
+ PACKAGE_NAME + "/files/" +
array[array.length - 1 ]); }
catch
(IOException e) { //
TODO Auto-generated catch block e.printStackTrace(); } PackageInstallObserver
observer = new
PackageInstallObserver(); pm.installPackage(mPackageURI,
observer, installFlags, null ); //
context.deleteFile(array[array.length-1]); } class
PackageInstallObserver extends
IPackageInstallObserver.Stub { public
void
packageInstalled(String packageName, int
returnCode) { Message
msg = mHandler.obtainMessage(INSTALL_COMPLETE); msg.arg1
= returnCode; mHandler.sendMessage(msg); } } private
File createTempPackageFile(String filePath) { File
tmpPackageFile; int
i = filePath.lastIndexOf( "/" ); String
tmpFileName; if
(i != - 1 )
{ tmpFileName
= filePath.substring(i + 1 ); }
else
{ tmpFileName
= filePath; } FileOutputStream
fos; try
{ //
MODE_WORLD_READABLE=1 fos
= context.openFileOutput(tmpFileName, 1 ); }
catch
(FileNotFoundException e1) { Log.e( "Installer" ,
"Error
opening file "
+ tmpFileName); return
null ; } try
{ fos.close(); }
catch
(IOException e) { Log.e( "Installer" ,
"Error
opening file "
+ tmpFileName); return
null ; } tmpPackageFile
= context.getFileStreamPath(tmpFileName); File
srcPackageFile = new
File(filePath); if
(!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) { return
null ; } return
tmpPackageFile; } public
void
makeTempCopyAndInstall(Uri mPackageURI) { mPkgInfo
= getPackageInfo(mPackageURI); System.out.println( "package="
+ mPkgInfo.applicationInfo.packageName); System.out.println( "copy
file="
+ mPackageURI.getPath()); File
mTmpFile = createTempPackageFile(mPackageURI.getPath()); if
(mTmpFile == null )
{ //
display a dialog Log.e( "Installer" , "Error
copying file locally. Failed Installation" ); //
showDialogInner(DLG_OUT_OF_SPACE); return ; } this .mPackageURI
= Uri.parse( "file://"
+ mTmpFile.getPath()); } public
PackageParser.Package getPackageInfo(Uri packageURI) { final
String archiveFilePath = packageURI.getPath(); PackageParser
packageParser = new
PackageParser(archiveFilePath); File
sourceFile = new
File(archiveFilePath); DisplayMetrics
metrics = new
DisplayMetrics(); metrics.setToDefaults(); return
packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0 ); } } |
在程序中的调用方式:this为Context,path为安装包的绝对路径
01
02
03
|
MyPackageInstaller
mpi = new
MyPackageInstaller( this ); mpi.makeTempCopyAndInstall(Uri.parse(path)); mpi.installPackage(); |
这种方式需要在源码下面编译apk,并将apk放入/system/app目录下面。
二.通过shell命令实现
首先,在java中实现安装和卸载apk的命令
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public
class
PackageInstaller { public
void
unInstallApp(String packageName){ try
{ Runtime.getRuntime().exec( "pm
uninstall " +packageName); }
catch
(IOException e) { e.printStackTrace(); } } public
void
installApp(String appPath){ try
{ Runtime.getRuntime().exec( "pm
install " +appPath); }
catch
(IOException e) { e.printStackTrace(); } } public
void
reInstallApp(String appPath){ try
{ Runtime.getRuntime().exec( "pm
install -r " +appPath); }
catch
(IOException e) { e.printStackTrace(); } } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
StartMain { private
static
final
String INSTALL_ACTION_LABEL = "install" ; private
static
final
String REINSTALL_ACTION_LABEL = "reinstall" ; private
static
final
String UNINSTALL_ACTION_LABEL = "uninstall" ; public
static
void
main(String args[]){ if (args== null ||args.length< 2 )
return ; PackageInstaller
pi= new
PackageInstaller(); if (args[ 0 ].equals(INSTALL_ACTION_LABEL)){ pi.installApp(args[ 1 ]); } if (args[ 0 ].equals(REINSTALL_ACTION_LABEL)){ pi.reInstallApp(args[ 1 ]); } else
if (args[ 0 ].equals(UNINSTALL_ACTION_LABEL)){ pi.unInstallApp(args[ 1 ]); } } } |
然后再源码环境下将该java程序编译为jar包
2.将编译好的jar包放入程序的assets目录下面,通过以下代码在程序中将该jar文件拷贝到/data/data/package/files/目录下面
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
try
{ AssetManager
assetManager = context.getResources().getAssets(); InputStream
in = assetManager.open(JAR_NAME); if
(in == null )
{ return ; } int
length = in.available(); byte
fileByte[] = new
byte [length]; in.read(fileByte,
0 ,
fileByte.length); in.close(); OutputStream
out = context.openFileOutput(JAR_NAME, Context.MODE_WORLD_READABLE
| Context.MODE_WORLD_WRITEABLE); out.write(fileByte); out.close(); }
catch
(Exception e) { e.printStackTrace(); } |
在有root权限的情况下,可以在shell中执行该jar包来进行安装和卸载:
01
02
|
String
exportClassPath = "export
CLASSPATH=/data/data/" +
context.getPackageName() + "/files/installpackagejar.jar" ; |
01
|
String
INSTALL_ACTION_CMD = "
exec app_process /system/bin packageName.StartMain install " ; |
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public
boolean
installApp(String path) { File
temp = new
File(path); if
(!temp.exists()) return
false ; String
cmd[] = { exportClassPath, INSTALL_ACTION_CMD + path }; try
{ consoleExec(cmd); }
catch
(IOException e) { e.printStackTrace(); return
false ; } return
true ; } |
01
02
03
04
05
06
07
08
09
10
|
private
void
consoleExec(String[] cmd) throws
IOException { Process
process = Runtime.getRuntime().exec( "su" ); DataOutputStream
os = new
DataOutputStream(process.getOutputStream()); for
( int
i = 0 ;
i < cmd.length; i++) { os.writeBytes(cmd<i>
+ "
" ); } os.writeBytes( "exit
" ); os.flush(); os.close(); }</i> |