android静默方式来安装apk

转自: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系统,可以解决。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值