转自:http://www.open-open.com/lib/view/open1328536451483.html
参考:http://blog.csdn.net/codenon/article/details/6052914
通常情况下,android是没有提供静默方式的上层接口,我们需要在android源代码下来调用这个隐藏的接口来完成静默安装。
最重要的就是参考android系统目录下的packages/apps/PackageInstaller,
当中有两个文件 PackageInstallerActivity.java,InstallAppProgress.java ,前者就是我们通常看到的带有提示对话框的安装应用程序,后者是点确定安装后调用的intent。
现提供一个静默安装的关键类,该类在android2.2下成功编译, 其中通过循环调用接口instatllBatch则可实现批量安装
当然最后的应用程序别忘记添加权限
1 | < uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" /> |
2 | < uses-permission android:name = "android.permission.INSTALL_PACKAGES" /> |
3 | < uses-permission android:name = "android.permission.DELETE_PACKAGES" /> |
4 | < uses-permission android:name = "android.permission.CLEAR_APP_CACHE" /> |
5 | < uses-permission android:name = "android.permission.READ_PHONE_STATE" /> |
6 | < uses-permission android:name = "android.permission.CLEAR_APP_USER_DATA" /> |
001 | package com.android.util; |
002 |
003 | import java.io.File; |
004 |
005 | import java.io.FileNotFoundException; |
006 |
007 | import java.io.FileOutputStream; |
008 |
009 | import java.io.IOException; |
010 |
011 | import android.content.Context; |
012 |
013 | import android.content.Intent; |
014 |
015 | import android.content.pm.PackageInfo; |
016 |
017 | import android.content.pm.PackageManager; |
018 |
019 | import android.content.pm.PackageManager.NameNotFoundException; |
020 |
021 | import android.content.pm.ApplicationInfo; |
022 |
023 | import android.content.pm.PackageParser; |
024 |
025 | import android.net.Uri; |
026 |
027 | import android.util.Log; |
028 |
029 | import android.util.DisplayMetrics; |
030 |
031 | import android.content.pm.IPackageInstallObserver; |
032 |
033 | import android.content.pm.IPackageDeleteObserver; |
034 |
035 | import android.os.FileUtils; |
036 |
037 | import android.os.Handler; |
038 |
039 | import android.os.Message; |
040 |
041 | |
042 |
043 | public class PackageInstaller { |
044 |
045 | |
046 |
047 | private File mTmpFile; |
048 |
049 | private final int INSTALL_COMPLETE = 1 ; |
050 |
051 | final static int SUCCEEDED = 1 ; |
052 |
053 | final static int FAILED = 0 ; |
054 |
055 | private final static String TAG = "PackInstaller" ; |
056 |
057 | private Context mContext; |
058 |
059 | private ApplicationInfo mAppInfo; |
060 |
061 | public PackageInstaller(Context context) { |
062 |
063 | mContext = context; |
064 |
065 | } |
066 |
067 | public void install(String path,String packageName){ |
068 |
069 | Intent intent = new Intent(Intent.ACTION_VIEW); |
070 |
071 | intent.setDataAndType(Uri.fromFile( new File(path)), |
072 |
073 | "application/vnd.android.package-archive" ); |
074 |
075 | mContext.startActivity(intent); |
076 |
077 | } |
078 |
079 | |
080 |
081 | public void instatllBatch(String path) { |
082 |
083 | Log.i(TAG, "path=" + path); |
084 |
085 | int installFlags = 0 ; |
086 |
087 | Uri mPackageURI = Uri.fromFile( new File(path)); |
088 |
089 | PackageParser.Package mPkgInfo = getPackageInfo(mPackageURI); |
090 |
091 | mAppInfo = mPkgInfo.applicationInfo; |
092 |
093 | String packageName = mAppInfo.packageName; |
094 |
095 | Log.i(TAG, "====install packageName =" +packageName); |
096 |
097 | PackageManager pm = mContext.getPackageManager(); |
098 |
099 | try { |
100 |
101 | PackageInfo pi = pm.getPackageInfo(packageName, |
102 |
103 | PackageManager.GET_UNINSTALLED_PACKAGES); |
104 |
105 | if (pi != null ) { |
106 |
107 | installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; |
108 |
109 | } |
110 |
111 | } catch (NameNotFoundException e) { |
112 |
113 | } |
114 |
115 | if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0 ) { |
116 |
117 | Log.w(TAG, "Replacing package:" + packageName); |
118 |
119 | } |
120 |
121 | |
122 |
123 | PackageInstallObserver observer = new PackageInstallObserver(); |
124 |
125 | pm.installPackage(mPackageURI, observer, installFlags, |
126 |
127 | packageName); |
128 |
129 | } |
130 |
131 | private class PackageInstallObserver extends IPackageInstallObserver.Stub { |
132 |
133 | public void packageInstalled(String packageName, int returnCode) { |
134 |
135 | // Message msg = mHandler.obtainMessage(INSTALL_COMPLETE); |
136 |
137 | // msg.arg1 = returnCode; |
138 |
139 | // mHandler.sendMessage(msg); |
140 |
141 | Log.i(TAG, "====INSTALL_COMPLETE" ); |
142 |
143 | } |
144 |
145 | } |
146 |
147 | private class PackageDeleteObserver extends IPackageDeleteObserver.Stub { |
148 |
149 | public void packageDeleted( boolean succeeded) { |
150 |
151 | // Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE); |
152 |
153 | // msg.arg1 = succeeded?SUCCEEDED:FAILED; |
154 |
155 | // mHandler.sendMessage(msg); |
156 |
157 | Log.i(TAG, "====UNINSTALL_COMPLETE" ); |
158 |
159 | } |
160 |
161 | } |
162 |
163 | public void uninstall(String packageName){ |
164 |
165 | Uri packageURI = Uri.parse( "package:" + packageName); |
166 |
167 | Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, |
168 |
169 | packageURI); |
170 |
171 | mContext.startActivity(uninstallIntent); |
172 |
173 | } |
174 |
175 | |
176 |
177 | public void uninstallBatch(String packageName) { |
178 |
179 | PackageDeleteObserver observer = new PackageDeleteObserver(); |
180 |
181 | mContext.getPackageManager().deletePackage(packageName, observer, 0 ); |
182 |
183 | |
184 |
185 | } |
186 |
187 | /* |
188 |
189 | * Utility method to get package information for a given packageURI |
190 |
191 | */ |
192 |
193 | public PackageParser.Package getPackageInfo(Uri packageURI) { |
194 |
195 | final String archiveFilePath = packageURI.getPath(); |
196 |
197 | PackageParser packageParser = new PackageParser(archiveFilePath); |
198 |
199 | File sourceFile = new File(archiveFilePath); |
200 |
201 | DisplayMetrics metrics = new DisplayMetrics(); |
202 |
203 | metrics.setToDefaults(); |
204 |
205 | PackageParser.Package pkg = packageParser.parsePackage(sourceFile, |
206 |
207 | archiveFilePath, metrics, 0); |
208 |
209 | // Nuke the parser reference. |
210 |
211 | packageParser = null; |
212 |
213 | return pkg; |
214 |
215 | } |
216 |
217 | /* |
218 |
219 | * Utility method to get application information for a given packageURI |
220 |
221 | */ |
222 |
223 | public ApplicationInfo getApplicationInfo(Uri packageURI) { |
224 |
225 | final String archiveFilePath = packageURI.getPath(); |
226 |
227 | PackageParser packageParser = new PackageParser(archiveFilePath); |
228 |
229 | File sourceFile = new File(archiveFilePath); |
230 |
231 | DisplayMetrics metrics = new DisplayMetrics(); |
232 |
233 | metrics.setToDefaults(); |
234 |
235 | PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0 ); |
236 |
237 | if (pkg == null ) { |
238 |
239 | return null ; |
240 |
241 | } |
242 |
243 | return pkg.applicationInfo; |
244 |
245 | } |
246 |
247 | private Handler mHandler = new Handler() { |
248 |
249 | public void handleMessage(Message msg) { |
250 |
251 | switch (msg.what) { |
252 |
253 | case INSTALL_COMPLETE: |
254 |
255 | if (msg.arg1 == SUCCEEDED) { |
256 |
257 | |
258 |
259 | } else {} |
260 |
261 | break ; |
262 |
263 | default : |
264 |
265 | break ; |
266 |
267 | } |
268 |
269 | } |
270 |
271 | }; |
272 |
273 | } |
(1)在网上搜寻该问题的解决方法,且查阅android开发文档,没有发现可以实现该功能的显示API调用,网络上很多人请教同样的问题,但都没有能够实现解答;说是android为了用户的安全,已屏蔽该实现该方法的功能,第三方法应用是无法实现静默安装的。
(2)然后自己试图去看看android实现普通安装程序的源码文件,能否找到解决的办法,打算绕过普通安装时的提示框,直接调用通过确认后调用的函数进行安装;在查看android应用程序的普通安装过程后,发现应用程序安装过程的方法调用过程为:首先进入到com/android/packageinstaller/PackageInstallerActivity.java这个Activity中,在这个Activity中首先检查所欲安装的程序是否是正确的安装文件,以及当前系统中是否已安装了此应用程序,提示用户是否重复安装,另外还获取所欲安装的程序所讲用到的权限,然后将这些信息通过一个对话框提示给用户,当用户确定安装时,启动com.android.packageinstaller.InstallAppProgress.java这个Activity,在这个Activity中,调用
android.content.pm.PackageManager.installPackage(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName)进行安装应用程序,在InstallAppProgress中得到的PackageManager是通过PackageManager pm = getPackageManager()得到的,得到的对象是一个android.app.ContextImpl.ApplicationPackageManager对象,而
ApplicationPackageManager对象经过封装,
ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
}
其installPackage方法为
@Override
public void installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,
String installerPackageName) {
try {
mPM.installPackage(packageURI, observer, flags, installerPackageName);
} catch (RemoteException e) {
// Should never happen!
}
}
可见调用的installPackage方法为 IPackageManager.installPackage(packageURI, observer, flags, installerPackageName);
在ContextImpl中,由IPackageManager pm = ActivityThread.getPackageManager()获得IPackageManager实例对象;在ActivityThread.getPackageManager()方法中,
static IPackageManager sPackageManager;
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
最终得到的installPackage确实是IPackageManager.installPackage方法;
因为class PackageManagerService extends IPackageManager.Stub所以IPackageManager.installPackage调用的是:PackageManagerService.java (frameworks/base/services/java/com/android/server)文件中的
/* Called when a downloaded package installation has been confirmed by the user */
public void installPackage(
final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
installPackage(packageURI, observer, flags, null);
}
(这里不明白为何IPackageManager.installPackage方法调用的是PackageManagerService.java,只是在网上的一篇文章中它给出了上面的原因,因为class PackageManagerService extends IPackageManager.Stub,我不明白,但也找不到其他的函数,通过PackageManagerService.java的源码,可以看出它确实是进行应用程序安装的,所以应该可以确定最终调用的方法就是
PackageManagerService.installPackage(final Uri packageURI, final IPackageInstallObserver observer, final int flags))
于是考虑如何得到PackageManagerService.installPackage(),考虑通过反射的方法得到installPackage(),但其中难以得到的是其参数中的IPackageInstallObserver类型,IPackageInstallObserver是由aidl文件定义的,通过aidl文件的特性,将IPackageInstallObserver.aidl文件拷到本地程序中,可以得到类IPackageInstallObserver.calss,通过它反射出installPackage()方法,但在invoke该方法时,却无法得到IPackageInstallObserver的实例对象,IPackageInstallObserver的实例对象必须通过
IPackageInstallObserver.Stub.asInterface(IBinder binder)方式得到,无法得到与其绑定的IBinder对象,因而无法执行反射出来的方法;另外PackageManagerService.installPackage()似乎是不能被第三方应用程序执行的,有权限的限制,这从下面的实例中似乎可以得到证实。
(3)在程序中执行Runtime.getRuntime().exec("pm install -r " + new File(Environment.getExternalStorageDirectory(),
"download/Shuffle-1.6.3.apk")); 进行安装,这个命令的执行在 com.android.commands.pm.Pm中,直接调用IPackageManager.installPackage(Uri.fromFile(new File(apkFilePath)), obs, installFlags,installerPackageName)方法,在此方法中,
IPackageManager mPm;
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
且class PackageManagerService extends IPackageManager.Stub
所以IPackageManager.installPackage调用的是:PackageManagerService.java (frameworks/base/services/java/com/android/server)文件中的
/* Called when a downloaded package installation has been confirmed by the user */
public void installPackage(
final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
installPackage(packageURI, observer, flags, null);
}
在此方法执行中会出现 Not granting permission android.permission.DELETE_PACKAGES错误,这应该是该权限不能授给第三方应用,因而在程序中不能执行,与android中普通安装应用程序最终调用的方法是相同的,但是却对第三方应用是没有权限执行的。。
(4) 另外解决思路:
1> 使用android:sharedUserId="android.uid.system"属性来使应用程序获得系统权限,看看是否能够执行行Runtime.getRuntime().exec("pm install -r ... ")方法。
2> 阅读android实现应用程序安装更底层的代码,看看能否可以调用的底层方法进行安装或者自己实现一个安装程序的代码,但这可能性不大,因为这涉及到android更底层的调用,
肯定会有一定的权限限制。
3> 在网上看到一个文件管理程序,据说是可以实现批量寂寞安装应用程序,但说明运行时需要用户确定得到手机的root权限,所以没有太大意义。
4> 定制自己的android系统,可以解决。