现在市面上各大手机的应用市场,都有静默安装和卸载的功能。当然,个人认为,这是一个流氓行为,可能你会莫名其妙的就安装了一堆应用。像360手机助手、豌豆荚之类的,虽然很像静默安装,其实不是。他们一是通过获取root权限,用pm install命令来静默安装apk的;二是通过智能安装服务,也就是Android中的无障碍服务,模拟点击事件来安装应用。

现在我们只分析Android中的静默安装和卸载,也就是PackageInstaller源码,源码版本5.0.2

PackageInstaller 源码目录/packages/apps/PackageInstaller。

分析AndroidManifest文件

首先我们看下AndroidManifest文件:
apps/PackageInstaller/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.packageinstaller"><original-package android:name="com.android.packageinstaller" /><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" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MANAGE_USERS" /><uses-permission android:name="android.permission.GRANT_REVOKE_PERMISSIONS" /><uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /><uses-permission android:name="android.permission.READ_INSTALL_SESSIONS" /><application android:label="@string/app_name"android:allowBackup="false"android:theme="@style/Theme.DialogWhenLarge"android:supportsRtl="true"><activity android:name=".PackageInstallerActivity"android:configChanges="orientation|keyboardHidden|screenSize"android:excludeFromRecents="true"><intent-filter><action android:name="android.intent.action.VIEW" /><action android:name="android.intent.action.INSTALL_PACKAGE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="file" /><data android:mimeType="application/vnd.android.package-archive" /></intent-filter><intent-filter><action android:name="android.intent.action.INSTALL_PACKAGE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="file" /><data android:scheme="package" /></intent-filter><intent-filter><action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity><activity android:name=".InstallAppProgress"android:configChanges="orientation|keyboardHidden|screenSize"android:exported="false" /><activity android:name=".UninstallerActivity"android:configChanges="orientation|keyboardHidden|screenSize"android:excludeFromRecents="true"android:theme="@style/Theme.AlertDialogActivity"><intent-filter><action android:name="android.intent.action.DELETE" /><action android:name="android.intent.action.UNINSTALL_PACKAGE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="package" /></intent-filter></activity><activity android:name=".UninstallAppProgress"android:configChanges="orientation|keyboardHidden|screenSize"android:exported="false" /><activity android:name=".GrantActivity"android:configChanges="orientation|keyboardHidden|screenSize"android:excludeFromRecents="true"android:theme="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"><intent-filter><action android:name="android.content.pm.action.REQUEST_PERMISSION" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity></application>
</manifest> 

请注意:android.permission.INSTALL_PACKAGES和android.permission.DELETE_PACKAGES权限,一个是安装权限,一个是卸载权限。

有五个Activity,PackageInstallerActivity(接受安装)、InstallAppProgress(安装进度条)、UninstallerActivity(接受卸载)、UninstallAppProgress(卸载进度条)、GrantActivity(询问用户权限)。

由于所有的Activity都没有指定android.intent.action.MAIN和android.intent.category.LAUNCHER标签,所有我们在桌面上看不到任何应用程序的图标。

PackageInstallerActivity指定了三个intent-filter,android.intent.action.INSTALL_PACKAGE是我们调用的Action,不过我们需要指定data,一种是scheme为file,必须要指定mimeType为application/vnd.android.package-archive;一种是scheme为package就ok了。

// 下面是安装的两种调用方式
// 第一种
Intent intent = new Intent("android.intent.action.INSTALL_PACKAGE");
intent.setDataAndType(Uri.fromFile(new File("/weixin.apk")), "application/vnd.android.package-archive");
startActivity(intent);// 第二种
Intent intent = new Intent("android.intent.action.INSTALL_PACKAGE");
intent.setData("package:com.tencent.weixin");
startActivity(intent);

至于UninstallerActivity,只指定一个intent-filter,scheme也是为file,不过却有两个Action,android.intent.action.DELETE和android.intent.action.UNINSTALL_PACKAGE。所以,我们也有两种方法可以调起来它。

// 下面是卸载的两种调用方式
// 第一种
Intent intent = new Intent("android.intent.action.DELETE");
intent.setData("package:com.tencent.weixin");
startActivity(intent);// 第二种
Intent intent = new Intent("android.intent.action.UNINSTALL_PACKAGE");
intent.setData("package:com.tencent.weixin");
startActivity(intent);

分析静默安装

PackageInstallerActivity类

我们来看下PackageInstallerActivity类的代码,代码也不是特别多,700多行,从onCreate()方法开始,onCreate()的代码是比较多的,我们就挑些重点的来看。

    ....final PackageUtil.AppSnippet as;if ("package".equals(mPackageURI.getScheme())) {mInstallFlowAnalytics.setFileUri(false);try {mPkgInfo = mPm.getPackageInfo(mPackageURI.getSchemeSpecificPart(),PackageManager.GET_PERMISSIONS | PackageManager.GET_UNINSTALLED_PACKAGES);} catch (NameNotFoundException e) {}if (mPkgInfo == null) {Log.w(TAG, "Requested package " + mPackageURI.getScheme()+ " not available. Discontinuing installation");showDialogInner(DLG_PACKAGE_ERROR);setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);mInstallFlowAnalytics.setPackageInfoObtained();mInstallFlowAnalytics.setFlowFinished(InstallFlowAnalytics.RESULT_FAILED_PACKAGE_MISSING);return;}as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),mPm.getApplicationIcon(mPkgInfo.applicationInfo));} else {mInstallFlowAnalytics.setFileUri(true);final File sourceFile = new File(mPackageURI.getPath());PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);// Check for parse errorsif (parsed == null) {Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");showDialogInner(DLG_PACKAGE_ERROR);setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);mInstallFlowAnalytics.setPackageInfoObtained();mInstallFlowAnalytics.setFlowFinished(InstallFlowAnalytics.RESULT_FAILED_TO_GET_PACKAGE_INFO);return;}mPkgInfo = PackageParser.generatePackageInfo(parsed, null,PackageManager.GET_PERMISSIONS, 0, 0, null,new PackageUserState());mPkgDigest = parsed.manifestDigest;as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);}...

mPackageURI = intent.getData();获取待安装Android应用的路径或package。接下来需要验证scheme是否合法,也就是是否为file或者package。主要要获取PackageInfo对象的实例,因为这个对象可以获取到待安装应用程序的包名、应用名称、图标…一系列的相关信息。

PackageUtil.AppSnippet是一个静态的内部类,里面只有两个字段,存放的是label和icon,也就是我们安装的时候看见的应用名称和图标。

接下来有一步很重要,就是检查”未知来源”是否开启了,如果开启,就进入initiateInstall()方法,否则,就弹窗提示。

       if ((requestFromUnknownSource) && (!isInstallingUnknownAppsAllowed())) {showDialogInner(DLG_UNKNOWN_APPS);mInstallFlowAnalytics.setFlowFinished(InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);return;}
    // 判断未知来源是否被允许private boolean isInstallingUnknownAppsAllowed() {UserManager um = (UserManager) getSystemService(USER_SERVICE);boolean disallowedByUserManager = um.getUserRestrictions().getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, false);boolean allowedBySecureSettings = Settings.Secure.getInt(getContentResolver(),Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;return (allowedBySecureSettings && (!disallowedByUserManager));}

这里面是有两个变量来控制的,一个requestFromUnknownSource和isInstallingUnknownAppsAllowed(),在5.0以前,是只有isInstallingUnknownAppsAllowed()来控制的。它是通过读取Content Provider,来获取设置中的”未知来源”是否被选中来判断的。

小技巧:我们有时需要往系统里面设置一些标志位,清除应用缓存不希望被清除,可以尝试用下Settings.Secure这个类,它提供了一套读写方法,是相当于键值对形式的。只有重置机器它才会被清除的,不过需要响应的权限。

接下来我们要分析initiateInstall()方法,你会发现,除了获取ApplicationInfo对象实例,然后就调用了startInstallConfirm()方法。这个方法显示了安装窗口的应用信息、权限列表、安装、取消按钮等,我们安装时看见的权限信息,就在AppSecurityPermissions类中,AppSecurityPermissions是一个组件,封装了一些处理权限的功能,AppSecurityPermissions.getPermissionsView()方法获取显示不同的权限View对象,ScrollView通过addView()直接把它添加到其中,然后显示出来。

startInstallConfirm()方法获取了三种权限:

// 获取与隐私相关权限的数量
final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
// 获取与设备相关权限的数量
final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
// 新加入的权限
final int new = perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW);

点击安装,其实是打开了InstallAppProgress Activity,由它来执行安装操作,当然会传递一些信息过去,由Intent携带过去。

InstallAppProgress类

InstallAppProgress类代码很少,就是安装和安装过后的反馈信息。其实,静默安装也就是在这其中实现,前面的都是一些拦截、校验的操作,要实现自己的静默安装,只要调用跟InstallAppProgress类中一样的API即可。

public void onCreate(Bundle icicle) {super.onCreate(icicle);Intent intent = getIntent();// 获取ApplicationInfo对象mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);mInstallFlowAnalytics.setContext(this);// 获取要安装的UrimPackageURI = intent.getData();final String scheme = mPackageURI.getScheme();if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {mInstallFlowAnalytics.setFlowFinished(InstallFlowAnalytics.RESULT_FAILED_UNSUPPORTED_SCHEME);throw new IllegalArgumentException("unexpected scheme " + scheme);}// 进行一些初始化工作,以及安装Andrroid应用initView();}

onCreate()方法主要获取上个界面传递过来的一些信息,获取mAppInfo对象和mPackageURI安装的uri,验证下scheme,调用initView()方法开始安装。

initView()方法的代码就很多了,提取精炼的来分析。

try {// 如果待安装程序已经安装,则返回PackageInfo对象,否则返回nullPackageInfo pi = pm.getPackageInfo(mAppInfo.packageName,PackageManager.GET_UNINSTALLED_PACKAGES);if (pi != null) {// 如果Android程序已经安装,设置安装模式为更新installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;}} catch (NameNotFoundException e) {}

首先,我们需要获取PackageInfo对象,判断程序是安装还是为更新。应用程序是安装还是更新主要是通过installFlags变量来控制的(很重要)。

VerificationParams verificationParams = new VerificationParams(null, originatingURI,referrer, originatingUid, manifestDigest);PackageInstallObserver observer = new PackageInstallObserver();if ("package".equals(mPackageURI.getScheme())) {try {// 更新Android应用pm.installExistingPackage(mAppInfo.packageName);observer.packageInstalled(mAppInfo.packageName,PackageManager.INSTALL_SUCCEEDED);} catch (PackageManager.NameNotFoundException e) {observer.packageInstalled(mAppInfo.packageName,PackageManager.INSTALL_FAILED_INVALID_APK);}} else {// 安装或更新应用pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,installerPackageName, verificationParams, null);}

这段代码就是静默安装了。如该scheme为package的话,就是更新Android应用, pm.installExistingPackage(mAppInfo.packageName);否则就是安装或更新应用,pm.installPackageWithVerificationAndEncryption(mPackageURI,observer, installFlags, installerPackageName, verificationParams, null)。

其中installExistingPackage()和installPackageWithVerificationAndEncryption()都是PackageManager类的方法,都可以实现静默安装,也就是在安装过程中不会出现任何提示。不过这两个都是为hide的,普通应用不能调用,只有系统应用才能调用。

关于安装信息的反馈,是通过PackageInstallObserver类,就是一个监听器,回调一个Message,通过Handler发出来。

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 Handler mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case INSTALL_COMPLETE:if (msg.arg1 == PackageManager.INSTALL_SUCCEEDED) {// 成功安装应用程序Toast.makeText(getApplicationContext(), "成功安装应用程序", Toast.LENGTH_SHORT).show();} else if (msg.arg1 == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE) {// 由于空间不足而导致安装失败Toast.makeText(getApplicationContext(), "由于空间不足而导致安装失败", Toast.LENGTH_SHORT).show();} else {// 由于其他原因导致安装失败Toast.makeText(getApplicationContext(), "由于其他原因导致安装失败", Toast.LENGTH_SHORT).show();getExplanationFromErrorCode(msg.arg1);}break;default:break;}}};private int getExplanationFromErrorCode(int errCode) {Log.d(TAG, "Installation error code: " + errCode);switch (errCode) {case PackageManager.INSTALL_FAILED_INVALID_APK:return R.string.install_failed_invalid_apk;case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES:return R.string.install_failed_inconsistent_certificates;case PackageManager.INSTALL_FAILED_OLDER_SDK:return R.string.install_failed_older_sdk;case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE:return R.string.install_failed_cpu_abi_incompatible;default:return -1;}}

反馈信息分为三种,成功安装应用程序、由于空间不足而导致安装失败、由于其他原因导致安装失败,其中由于其他原因导致安装失败,可以根据getExplanationFromErrorCode()方法,得到具体的失败原因,进行相对应的处理。

现在,我们已经理解啦静默安装的原理了,在安装程序之前的弹窗,是PackageInstaller故意搞出来了,我们只要去掉它,调用PackageManager的API就能实现自己的静默安装。

分析静默卸载

UninstallerActivity类

静默安装分析完了,现在就来分析下静默卸载吧。从UninstallerActivity类看起,相对于安装,卸载的这个类只有200多行代码,少很多。

 @Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle);...final Intent intent = getIntent();final Uri packageUri = intent.getData();// 获取要卸载程序的包名final String packageName = packageUri.getEncodedSchemeSpecificPart();final IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));mDialogInfo = new DialogInfo();mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);if (mDialogInfo.user == null) {mDialogInfo.user = android.os.Process.myUserHandle();}mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);// 获取ApplicationInfo对象try {mDialogInfo.appInfo = pm.getApplicationInfo(packageName,PackageManager.GET_UNINSTALLED_PACKAGES, mDialogInfo.user.getIdentifier());} catch (RemoteException e) {}...showConfirmationDialog();}

onCreate()方法,抽取啦一些主要代码出来,主要获取传递过来的包名,初始化Dialog,然后通过showConfirmationDialog()弹出Dialog。

UninstallAlertDialogFragment是一个DialogFragment,为UninstallerActivity的内部类,也就是显示的卸载对话框。点击”确定”,开始卸载,调用startUninstallProgress()方法。

void startUninstallProgress() {Intent newIntent = new Intent(Intent.ACTION_VIEW);newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);// 要求卸载该Android应用对于所有用户的程序和数据(Android 4.2开始支持多用户)newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);// appInfo 封装了要卸载程序的信息newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);// 允许卸载窗口返回是否卸载成功的标志if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);}// 指定负责卸载Android应用的窗口类newIntent.setClass(this, UninstallAppProgress.class);startActivity(newIntent);}

主要的卸载操作在UninstallAppProgress类中,而UninstallerActivity只是做卸载前的拦截作用,现在卸载窗口而已。

UninstallAppProgress类

这个类主要作用就是卸载APK,在onCreate()方法中有个东西很重要。

// 获取是否删除所有用户数据的标志位
mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);

就是在卸载的时候,是否删除用户数据。主要的卸载工作有initView()方法去完成,提炼主要代码分析。

        IPackageManager packageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));// 创建卸载监听器PackageDeleteObserver observer = new PackageDeleteObserver();// 静默卸载Android应用try {packageManager.deletePackageAsUser(mAppInfo.packageName, observer,mUser.getIdentifier(),mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);} catch (RemoteException e) {// Shouldn't happen.Log.e(TAG, "Failed to talk to package manager", e);}

卸载应用程序主要通过deletePackageAsUser()方法,第一个参数是包名,第二个是卸载反馈的监听器,第三个是用户id,第四个是是否删除用户数据。PackageDeleteObserver监听器是用来接收卸载的反馈信息。

class PackageDeleteObserver extends IPackageDeleteObserver.Stub {public void packageDeleted(String packageName, int returnCode) {Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);msg.arg1 = returnCode;msg.obj = packageName;mHandler.sendMessage(msg);}}
    private Handler mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case UNINSTALL_COMPLETE:...// 卸载完成int resultCode = msg.arg1;final String packageName = (String) msg.obj;// 处理不需要返回卸载状态的情况switch (msg.arg1) {case PackageManager.DELETE_SUCCEEDED:// 成功卸载Android应用return;case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER:// 由于某些策略的原因导致了卸载失败break;case PackageManager.DELETE_FAILED_OWNER_BLOCKED:break;default:// 由于其他原因导致了卸载失败break;}break;...default:break;}}};

这是卸载结果的一些处理,我只列出了卸载的结果类型,其中涉及到一些UI操作,并没有展开。主要分为需要处理返回状态和不需要处理返回状态这两种情况,它们都会在处理完毕过后关闭这个窗口。

因为静默卸载和安装都是异步处理的,所有用户感觉不到,仍然可以做自己的事情,但是其实这个时候,系统是在高速运作,程序的安装和卸载是需要耗费cpu资源的,所以如果手机性能差的话,可能会感觉到卡吨…唉,流氓软件就是这样来的。

实现自己的静默安装与卸载

分析了这么多,我们应该来实现自己的静默安装和卸载了。由于静默安装和卸载的API是系统级别的,所以我们必须要用Android源码来编译的。在Eclipse下建Project,写个.mk文件,可以直接把PackageInstaller的copy过来,修改一下就好。
Android.mk文件

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES :=$(call all-java-files-under, src)LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4LOCAL_PACKAGE_NAME := ZhouSilenceInstaller
LOCAL_CERTIFICATE := platforminclude $(BUILD_PACKAGE)

AndroidManifest.xml文件,也copy一下PackageInstaller的,修改一些就好。主要,由于我们不需要安装前的提示框,所有我们只需要安装的就可以了,所以Activity的主题可以设置为

android:theme="@android:style/Theme.NoDisplay"

这个主题是不显示的,比透明主题更好。

反过来想一下,是不是可以用Service。这样会不会更好,实验证明,我感觉IntentService实现静默安装和卸载会更好。代码在后面会贴上的。

权限问题一定要注意

<!-- 安装权限 -->
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<!-- 卸载权限 -->
<uses-permission android:name="android.permission.DELETE_PACKAGES" />

这两个是最重要的,当然,其中还有一些读取sdcard的,关于获取权限列表的、用户信息的…

先看下清单文件吧~局部,具体的请下载查看吧。

        <!-- 静默安装服务 --><service
            android:name="com.zhou.silence.installer.InstallService"android:exported="true" ><intent-filter><action android:name="android.intent.action.VIEW.HIDE" /><action android:name="android.intent.action.INSTALL_PACKAGE_HIDE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="file" /><data android:mimeType="application/vnd.android.package-archive" /></intent-filter></service><!-- 静默卸载服务 --><service
            android:name="com.zhou.silence.installer.UninstallService"android:exported="true" ><intent-filter><action android:name="android.intent.action.DELETE" /><action android:name="android.intent.action.UNINSTALL_PACKAGE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="package" /></intent-filter></service>

当然,我也写了Activity安装、卸载的实现方式,我只贴上了Service的代码,因为我认为它比Activity更好。

静默安装 InstallService.java

package com.zhou.silence.installer;import java.io.File;import com.zhou.silence.installer.InstallerAct.PackageInstallObserver;import android.app.IntentService;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;
import android.util.DisplayMetrics;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ResolveInfo;
import android.content.pm.VerificationParams;
import android.content.pm.IPackageInstallObserver;public class InstallService extends IntentService {private PackageInfo mPkgInfo;private Uri mPackageURI;public InstallService() {super("InstallService");}@Overrideprotected void onHandleIntent(Intent intent) {synchronized (this) {handleIntent(intent);}}private void handleIntent(Intent intent) {try {Toast.makeText(getApplicationContext(), "开始安装...", Toast.LENGTH_SHORT).show();PackageManager pm = getPackageManager();int installFlags = 0;// 获取要安装的UrimPackageURI = intent.getData();// 用File对象封装apk文件的路径final File sourceFile = new File(mPackageURI.getPath());// 创建封装包信息的Package对象PackageParser packageParser = new PackageParser();PackageParser.Package parsed = packageParser.parsePackage(sourceFile, 0);PackageUserState state = new PackageUserState();mPkgInfo = PackageParser.generatePackageInfo(parsed, null, PackageManager.GET_UNINSTALLED_PACKAGES, 0, 0, null, state);if (mPkgInfo == null) {// 如果Android程序已经安装,设置安装模式为更新installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;}VerificationParams verificationParams = new VerificationParams(null, null, null, VerificationParams.NO_UID, null);PackageInstallObserver observer = new PackageInstallObserver();// 安装或更新应用pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,mPkgInfo.applicationInfo.packageName, verificationParams, null);} catch (Exception e) {Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();e.printStackTrace();}}private final int INSTALL_COMPLETE = 1;private Handler mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case INSTALL_COMPLETE:if (msg.arg1 == PackageManager.INSTALL_SUCCEEDED) {// 成功安装应用程序Toast.makeText(getApplicationContext(), "成功安装应用程序", Toast.LENGTH_SHORT).show();} else if (msg.arg1 == PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE) {// 由于空间不足而导致安装失败Toast.makeText(getApplicationContext(), "由于空间不足而导致安装失败", Toast.LENGTH_SHORT).show();} else {// 由于其他原因导致安装失败Toast.makeText(getApplicationContext(), "由于其他原因导致安装失败", Toast.LENGTH_SHORT).show();}break;default:break;}}};// 异步安装事件监听器类class PackageInstallObserver extends IPackageInstallObserver.Stub {public void packageInstalled(String packageName, int returnCode) {Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);msg.arg1 = returnCode;mHandler.sendMessage(msg);}}}

静默卸载 UninstallService.java

package com.zhou.silence.installer;import com.zhou.silence.installer.UninstallAct.PackageDeleteObserver;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.IntentService;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.os.ServiceManager;
import android.os.UserManager;
import android.os.UserHandle;public class UninstallService extends IntentService {private static final int UNINSTALL_COMPLETE = 1;private UserHandle mUser;private boolean mAllUsers;private Intent intent;private Handler mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case UNINSTALL_COMPLETE:// 卸载完成int resultCode = msg.arg1;final String packageName = (String) msg.obj;// 处理不需要返回卸载状态的情况switch (msg.arg1) {case PackageManager.DELETE_SUCCEEDED:// 成功卸载Android应用Toast.makeText(getBaseContext(), "卸载成功", Toast.LENGTH_LONG).show();return;case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER:// 由于某些策略的原因导致了卸载失败Toast.makeText(getBaseContext(), "Uninstall failed because " + packageName + " is a device admin",Toast.LENGTH_LONG).show();break;case PackageManager.DELETE_FAILED_OWNER_BLOCKED:Toast.makeText(getBaseContext(), "Uninstall failed because owner blocked", Toast.LENGTH_LONG).show();break;default:// 由于其他原因导致了卸载失败Toast.makeText(getBaseContext(), "Uninstall failed for " + packageName + " with code " + msg.arg1,Toast.LENGTH_LONG).show();break;}break;default:break;}}};public UninstallService() {super("UninstallService");}@Overrideprotected void onHandleIntent(Intent intent) {synchronized (this) {this.intent = intent;handleIntent(intent);}}private void handleIntent(Intent intent) {Toast.makeText(getApplicationContext(), "正在卸载...", Toast.LENGTH_SHORT).show();final Uri packageUri = intent.getData();// 获取要卸载程序的包名final String packageName = packageUri.getEncodedSchemeSpecificPart();mUser = intent.getParcelableExtra(Intent.EXTRA_USER);if (mUser == null) {mUser = android.os.Process.myUserHandle();}// 获取是否删除所有用户数据的标志位mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);if (mAllUsers && UserHandle.myUserId() != UserHandle.USER_OWNER) {throw new SecurityException("Only owner user can request uninstall for all users");}IPackageManager packageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));PackageDeleteObserver observer = new PackageDeleteObserver();try {packageManager.deletePackageAsUser(packageName, observer, mUser.getIdentifier(),mAllUsers ? PackageManager.DELETE_ALL_USERS : 0);} catch (Exception e) {Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT).show();}}class PackageDeleteObserver extends IPackageDeleteObserver.Stub {public void packageDeleted(String packageName, int returnCode) {Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);msg.arg1 = returnCode;msg.obj = packageName;mHandler.sendMessage(msg);}}}

然后,我们可以用模块编译

mmm packages/apps/ZhouSilenceInstaller/adb install out/target/product/grouper/system/app/ZhouSilenceInstaller/ZhouSilenceInstaller.apk

就可以看见效果了,SUCCESS!

好了,至此,PackageInstaller的源码就分析完毕啦,也实现了自己的静默安装和卸载。其中发现4.0和5.0的源码区别还是很大的,也许你用的是6.0,可能和本文的代码有所出入,甚至可能连API都有些不同,请耐心分析,肯定会有所收获。

静默安装的demo:http://download.csdn.net/detail/u012301841/9613549
测试静默安装的demo:http://download.csdn.net/detail/u012301841/9613923

PackageInstaller 5.0源码分析静默安装与静默卸载相关推荐

  1. 《MapReduce 2.0源码分析与编程实战》一第1章 HBase介绍

    本节书摘来异步社区<MapReduce 2.0源码分析与编程实战>一书中的第1章,作者: 王晓华 责编: 陈冀康,更多章节内容可以访问云栖社区"异步社区"公众号查看. ...

  2. vue-cli 3.0 源码分析

    写在前面 其实最开始不是特意来研究 vue-cli 的源码,只是想了解下 node 的命令,如果想要了解 node 命令的话,那么绕不开 tj 写的 commander.js.在学习 commande ...

  3. Tomcat7.0源码分析——Session管理分析(下)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/beliefer/article/details/52451061 前言 在<Tomcat7.0 ...

  4. Tomcat7.0源码分析——Session管理分析(上)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/beliefer/article/details/52450268 前言 对于广大java开发者而言, ...

  5. Android6.0源码分析—— Zygote进程分析(补充)

    原文地址: http://blog.csdn.net/a34140974/article/details/50915307 此博文为<Android5.0源码分析-- Zygote进程分析> ...

  6. android6.0源码分析之Zygote进程分析

    在android6.0源码分析之Runtime的初始化一文中,对Zygote进程的初期的Runtime初始化过程进行了分析,在Runtime启动结束后,会对Zygote进程进行初始化,其它Java进程 ...

  7. android6.0源码分析之Runtime的初始化

    Android运行时作为android架构的一部分,起着非常重要的作用,它和核心库(Core Libraries)组成了Android运行时库层.本文将依据android源码对AndroidRunti ...

  8. android6.0源码分析之Camera2 HAL分析

    1.Camera HAL的初始化 Camera HAL的初始加载是在Native的CameraService初始化流程中的,而CameraService初始化是在Main_mediaServer.cp ...

  9. android6.0源码分析之Camera API2.0下的Preview(预览)流程分析

    1.Camera2 preview的应用层流程分析 preview流程都是从startPreview开始的,所以来看startPreview方法的代码: <code class="hl ...

  10. android6.0源码分析之Camera API2.0下的初始化流程分析

    1.Camera2初始化的应用层流程分析 Camera2的初始化流程与Camera1.0有所区别,本文将就Camera2的内置应用来分析Camera2.0的初始化过程.Camera2.0首先启动的是C ...

最新文章

  1. Xamarin SimplerCursorAdapter 适配器(三)
  2. linux网站配置文件,Linux网站服务器配置文件
  3. percentiles of live data capture
  4. lua学习笔记之语句
  5. systemverilog硬件设计及建模_3D建模和渲染都吃什么硬件?设计师该如何选购电脑...
  6. 爬虫goodreads数据_使用Python从Goodreads数据中预测好书
  7. Struts2的配置文件struts.xml的模板
  8. leetcode557. 反转字符串中的单词 III
  9. 开始→运行→输入的命令集锦( 菜鸟必读)
  10. Jsoup消除不受信任的HTML(用于防止XSS的攻击)
  11. 阿里道延:我对技术架构的理解与架构师角色的思考
  12. Linux 命令(18)—— screen 命令
  13. scrum角色及其职责介绍
  14. latex自动生成中文目录_LaTeX技巧907:目录与格式定制
  15. @media用法解释
  16. FTP 基础及连接跟踪
  17. Java输出英语字母对应的数字
  18. 1024,程序员节!快乐!
  19. ppt制作教程与原理介绍(学习记录)
  20. 从《雪白血红》说起(2)

热门文章

  1. Beijing54坐标系——Y坐标(6位数和8位数)区别
  2. java steam的缓冲区_SSM框架完结篇 SpringMVC与Steam前后台功能模块开发
  3. 利用FFmpegFrameGrabber截取视频中的某一帧图片压缩并旋转
  4. html渐变显示动画效果,css3实现背景动态渐变效果
  5. STM32CubeMX系列TIM
  6. 服务器装凤凰系统,云服务器安装凤凰os
  7. python矩阵连乘_动态规划:矩阵连乘问题
  8. 服务器双网卡连接内外网配置
  9. java实训小结_java实训心得体会(精选4篇)
  10. 大一java实训报告1500字_java实训报告两篇