Android_实现静默安装和卸载应用

简介: 转:http://www.cnblogs.com/ondream/archive/2012/04/13/2446138.html    前段时间做了一个批量安装卸载应用程序的小应用,由于安装卸载应用程序的部分API是隐藏的,所以必须在ubuntu下下载Android系统源码,并编译之后使用MM命令编译生成APK文件,其实也难。

转:http://www.cnblogs.com/ondream/archive/2012/04/13/2446138.html

  

前段时间做了一个批量安装卸载应用程序的小应用,由于安装卸载应用程序的部分API是隐藏的,所以必须在ubuntu下下载Android系统源码,并编译之后使用MM命令编译生成APK文件,其实也难。思路是这样的,在XX/packages/apps目录下有一个PackageInstaller的应用程序,Android机器中安装卸载都是由这个应用程序完成的。但是它没有批量安装和卸载的功能,如果要在自己的应用程序中添加批量安装和卸载的功能,其实很简单,只需要参考PakcageInstaller里面的安装卸载代码加个循环就可以了。但值得注意的是在编译的过程中必须复制PackageInstaller里面的Android.mk文件,修改文件为工程目录名。好了,废话不再多说,下面是关键代码

   1、 Android.mk文件

复制代码
LOCAL_PATH:= $(call my-dir)  
include $(CLEAR_VARS)  
  
LOCAL_MODULE_TAGS := optional  
  
LOCAL_SRC_FILES := $(call all-subdir-java-files)  
  
LOCAL_PACKAGE_NAME := PackageInstaller  
LOCAL_CERTIFICATE := platform  
  
include $(BUILD_PACKAGE)  
复制代码

 

 

重点是LOCAL_CERTIFICATE := platform要对

 

 

   2、PakcageInstaller.java文件(关键代码)

复制代码
package cn.ceadic.apkmgr;  
  
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.PackageManager.NameNotFoundException;  
import android.net.Uri;  
import android.util.Log;  
  
import android.content.pm.IPackageInstallObserver;  
import android.content.pm.IPackageDeleteObserver;  
import android.os.FileUtils;  
  
  
public class PackageInstaller {  
      
    private File mTmpFile;  
    private final String TMP_FILE_NAME = "tmpCopy.apk";  
  
    private final static String TAG = "PackInstaller";  
    private Context mContext;  
  
    public PackageInstaller(Context context) {  
        mContext = context;  
    }  
  
      
    public void install(String path,String packageName){  
         Intent intent = new Intent(Intent.ACTION_VIEW);  
         intent.setDataAndType(Uri.fromFile(new File(path)),  
         "application/vnd.android.package-archive");  
         mContext.startActivity(intent);  
    }  
      
    public void instatllBatch(String path, String packageName) {  
  
        Log.i(TAG, "path=" + path);  
        int installFlags = 0;  
        PackageManager pm = mContext.getPackageManager();  
        try {  
            PackageInfo pi = pm.getPackageInfo(packageName,  
                    PackageManager.GET_UNINSTALLED_PACKAGES);  
            if (pi != null) {  
                installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;  
            }  
        } catch (NameNotFoundException e) {  
        }  
        if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {  
            Log.w(TAG, "Replacing package:" + packageName);  
        }  
  
        // Create temp file before invoking install api  
        mTmpFile = createTempPackageFile(path);  
        if (mTmpFile == null) {  
            // Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);  
            // msg.arg1 = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
            // mHandler.sendMessage(msg);  
            return;  
        }  
        Uri mPackageURI = Uri.parse("file://" + mTmpFile.getPath());  
        String installerPackageName = mContext.getIntent().getStringExtra(  
                Intent.EXTRA_INSTALLER_PACKAGE_NAME);  
  
        PackageInstallObserver observer = new PackageInstallObserver();  
        pm.installPackage(mPackageURI, observer, installFlags,  
                installerPackageName);  
    }  
  
    private File createTempPackageFile(String filePath) {  
        File tmpPackageFile = mContext.getFileStreamPath(TMP_FILE_NAME);  
        if (tmpPackageFile == null) {  
            Log.w(TAG, "Failed to create temp file");  
            return null;  
        }  
        if (tmpPackageFile.exists()) {  
            tmpPackageFile.delete();  
        }  
        // Open file to make it world readable  
        FileOutputStream fos;  
        try {  
            fos = openFileOutput(TMP_FILE_NAME, MODE_WORLD_READABLE);  
        } catch (FileNotFoundException e1) {  
            Log.e(TAG, "Error opening file " + TMP_FILE_NAME);  
            return null;  
        }  
        try {  
            fos.close();  
        } catch (IOException e) {  
            Log.e(TAG, "Error opening file " + TMP_FILE_NAME);  
            return null;  
        }  
  
        File srcPackageFile = new File(filePath);  
        if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) {  
            Log.w(TAG, "Failed to make copy of file: " + srcPackageFile);  
            return null;  
        }  
        return tmpPackageFile;  
    }  
  
    private class PackageInstallObserver extends IPackageInstallObserver.Stub {  
        public void packageInstalled(String packageName, int returnCode) {  
            // Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);  
            // msg.arg1 = returnCode;  
            // mHandler.sendMessage(msg);  
            Log.i(TAG, "====INSTALL_COMPLETE");  
        }  
    }  
      
    private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {  
        public void packageDeleted(boolean succeeded) {  
//            Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);  
//            msg.arg1 = succeeded?SUCCEEDED:FAILED;  
//            mHandler.sendMessage(msg);  
            Log.i(TAG, "====UNINSTALL_COMPLETE");  
        }  
    }  
      
    public void uninstall(String packageName){  
        Uri packageURI = Uri.parse("package:" + packageName);  
        Intent uninstallIntent = new Intent(Intent.ACTION_DELETE,  
        packageURI);  
        mContext.startActivity(uninstallIntent);  
    }  
      
    public void uninstallBatch(String packageName) {  
        PackageDeleteObserver observer = new PackageDeleteObserver();  
        mContext.getPackageManager().deletePackage(packageName, observer, 0);  
          
    }  
}  
复制代码

 

   3、别忘记添加权限

复制代码
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />  
    <uses-permission android:name="android.permission.DELETE_PACKAGES" />  
    <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />  
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />  
    <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />  
复制代码

 


以上代码在Android2.1的SDK中编译通过,并正确批量安装卸载应用程序

 

转自:http://blog.csdn.net/tangcheng_ok/article/details/6681453

另外关键的是:你要有linux环境或其他可以mmm交叉编译的环境,因为这个用到的是隐藏api,SDK中没有的,ecilipse里面会提示包不存在,但交叉编译可以,如果你不知道mmm编译,请return。

调用uinstall会弹出卸载界面,而调用uninstallBatch不会弹出卸载界面。

在Windows下可下载模拟的unix环境cygwin

另外还可参考:

 

实现静默安装APK的两种方法

 android静默安装步骤:http://download.csdn.net/download/lyj286326189/4049858


 

android静默安装探讨
 

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

 

说了这么多,最后总结的是:修改android。mk文件并在linux下和系统源代码一起编译及签名为目标系统签名来生成apk,然后参照packegeinstaller源代码调用方法即可

确保或者获取手机root权限,然后执行

Runtime.getRuntime().exec("pm install -r " + filePath);

 

转自 http://hcq0618.blog.163.com/blog/static/178090351201222552215372/

欢迎各位同学加入 android 技术群 155595043

个人微博: http://weibo.com/338226333

弹窗安装

             Intent it = new Intent(Intent.ACTION_VIEW);
             it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             it.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
             context.startActivity(it);

 

 相关链接:http://blog.csdn.net/fenyush/article/details/6052914

              http://blog.csdn.net/sodino/article/details/6238818

               http://blog.sina.com.cn/s/blog_44e6424c0100zbhp.html

目录
相关文章
|
10月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
968 4
|
5月前
|
存储 Android开发
如何查看Flutter应用在Android设备上已被撤销的权限?
如何查看Flutter应用在Android设备上已被撤销的权限?
255 64
|
7月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
417 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
7月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
166 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
10月前
|
算法 Java 数据库
Android 应用的主线程在什么情况下会被阻塞?
【10月更文挑战第20天】为了避免主线程阻塞,我们需要合理地设计和优化应用的代码。将耗时操作移到后台线程执行,使用异步任务、线程池等技术来提高应用的并发处理能力。同时,要注意避免出现死循环、不合理的锁使用等问题。通过这些措施,可以确保主线程能够高效地运行,提供流畅的用户体验。
468 58
|
9月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
243 14
|
9月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
179 13
|
9月前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
10月前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
10月前
|
搜索推荐 开发工具 Android开发
打造个性化Android应用:从设计到实现的旅程
【10月更文挑战第26天】在这个数字时代,拥有一个能够脱颖而出的移动应用是成功的关键。本文将引导您了解如何从概念化阶段出发,通过设计、开发直至发布,一步步构建一个既美观又实用的Android应用。我们将探讨用户体验(UX)设计的重要性,介绍Android开发的核心组件,并通过实际案例展示如何克服开发中的挑战。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧,帮助您在竞争激烈的应用市场中脱颖而出。