Android 静默安装 静默卸载 探索之路

最近的在做APP版本更新,每次版本更新,弹出提示用户安装用户体验特不友好,行业对于静默安装也有不少大牛写了一下自己博客,看了这写大牛写的博客后,自己动手也写了一个APP,需要下载的同学可以自己下载。

首先,在探索这一项技术之前我要找到突破口我们先看下 安装APP的代码:


Intent intent = new Intent(Intent.ACTION_VIEW);
// 由于没有在Activity环境下启动Activity,设置下面的标签
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

//判读版本是否在7.0以上
if (Build.VERSION.SDK_INT >= 24) {
//参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致 参数3 共享的文件
Uri apkUri = FileProvider.getUriForFile(context, "com.juying.photographer.fileprovider", mFile);
//添加这一句表示对目标应用临时授权该Uri所代表的文件
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");

} else {
intent.setDataAndType(Uri.fromFile(mFile), "application/vnd.android.package-archive");
}
context.startActivity(intent)

之前看过这样一段代码:
PackageManager manager = this.getPackageManager();
PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
String version = info.versionName;
大概定位 google可能会把应用宝安装操作放在 PackageManager.java 文件中。

根据给这个思路 我们需要查看Google源码,我们在
\frameworks\base\core\java\android\content\pm找到了这个文件。


if ("install".equals(op)) {
runInstall();
return;
}


我们在查看这个方法

private void runInstall() {
int installFlags = 0;
int userId = UserHandle.USER_ALL;
String installerPackageName = null;

String opt;

String originatingUriString = null;
String referrer = null;
String abi = null;

while ((opt=nextOption()) != null) {
if (opt.equals("-l")) {
installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
} else if (opt.equals("-r")) {
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
} else if (opt.equals("-i")) {
installerPackageName = nextOptionData();
if (installerPackageName == null) {
System.err.println("Error: no value specified for -i");
return;
}
} else if (opt.equals("-t")) {
installFlags |= PackageManager.INSTALL_ALLOW_TEST;
} else if (opt.equals("-s")) {
// Override if -s option is specified.
installFlags |= PackageManager.INSTALL_EXTERNAL;
} else if (opt.equals("-f")) {
// Override if -s option is specified.
installFlags |= PackageManager.INSTALL_INTERNAL;
} else if (opt.equals("-d")) {
installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else if (opt.equals("--originating-uri")) {
originatingUriString = nextOptionData();
if (originatingUriString == null) {
System.err.println("Error: must supply argument for --originating-uri");
return;
}
} else if (opt.equals("--referrer")) {
referrer = nextOptionData();
if (referrer == null) {
System.err.println("Error: must supply argument for --referrer");
return;
}
} else if (opt.equals("--abi")) {
abi = checkAbiArgument(nextOptionData());
} else if (opt.equals("--user")) {
userId = Integer.parseInt(nextOptionData());
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}

if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_OWNER;
installFlags |= PackageManager.INSTALL_ALL_USERS;
}

final Uri verificationURI;
final Uri originatingURI;
final Uri referrerURI;

if (originatingUriString != null) {
originatingURI = Uri.parse(originatingUriString);
} else {
originatingURI = null;
}

if (referrer != null) {
referrerURI = Uri.parse(referrer);
} else {
referrerURI = null;
}

// Populate apkURI, must be present
final String apkFilePath = nextArg();
System.err.println("\tpkg: " + apkFilePath);
if (apkFilePath == null) {
System.err.println("Error: no package specified");
return;
}

// Populate verificationURI, optionally present
final String verificationFilePath = nextArg();
if (verificationFilePath != null) {
System.err.println("\tver: " + verificationFilePath);
verificationURI = Uri.fromFile(new File(verificationFilePath));
} else {
verificationURI = null;
}

LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
try {
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);

mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
installerPackageName, verificationParams, abi, userId);

synchronized (obs) {
while (!obs.finished) {
try {
obs.wait();
} catch (InterruptedException e) {
}
}
if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
System.out.println("Success");
} else {
System.err.println("Failure ["
+ installFailureToString(obs)
+ "]");
}
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
}
}

我们在快速锁定

LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
try {
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);

mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
installerPackageName, verificationParams, abi, userId);

synchronized (obs) {
while (!obs.finished) {
try {
obs.wait();
} catch (InterruptedException e) {
}
}
if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
System.out.println("Success");
} else {
System.err.println("Failure ["
+ installFailureToString(obs)
+ "]");
}
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
}

这个就是 静默安装的核心代码:

我们在看看 静默卸载的核心代码:

private void runUninstall() throws RemoteException {
        int flags = 0;
        int userId = UserHandle.USER_ALL;


        String opt;
        while ((opt=nextOption()) != null) {
            if (opt.equals("-k")) {
                flags |= PackageManager.DELETE_KEEP_DATA;
            } else if (opt.equals("--user")) {
                String param = nextArg();
                if (isNumber(param)) {
                    userId = Integer.parseInt(param);
                } else {
                    showUsage();
                    System.err.println("Error: Invalid user: " + param);
                    return;
                }
            } else {
                System.err.println("Error: Unknown option: " + opt);
                return;
            }
        }


        String pkg = nextArg();
        if (pkg == null) {
            System.err.println("Error: no package specified");
            showUsage();
            return;
        }


        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_OWNER;
            flags |= PackageManager.DELETE_ALL_USERS;
        } else {
            PackageInfo info;
            try {
                info = mPm.getPackageInfo(pkg, 0, userId);
            } catch (RemoteException e) {
                System.err.println(e.toString());
                System.err.println(PM_NOT_RUNNING_ERR);
                return;
            }
            if (info == null) {
                System.err.println("Failure - not installed for " + userId);
                return;
            }
            final boolean isSystem =
                    (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
            // If we are being asked to delete a system app for just one
            // user set flag so it disables rather than reverting to system
            // version of the app.
            if (isSystem) {
                flags |= PackageManager.DELETE_SYSTEM_APP;
            }
        }


        final LocalIntentReceiver receiver = new LocalIntentReceiver();
        mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);


        final Intent result = receiver.getResult();
        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                PackageInstaller.STATUS_FAILURE);
        if (status == PackageInstaller.STATUS_SUCCESS) {
            System.out.println("Success");
        } else {
            Log.e(TAG, "Failure details: " + result.getExtras());
            System.out.println("Failure ["
                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
        }
    }

核心代码如下

    final LocalIntentReceiver receiver = new LocalIntentReceiver();
        mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);


        final Intent result = receiver.getResult();
        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                PackageInstaller.STATUS_FAILURE);
        if (status == PackageInstaller.STATUS_SUCCESS) {
            System.out.println("Success");
        } else {
            Log.e(TAG, "Failure details: " + result.getExtras());
            System.out.println("Failure ["
                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
        }

我们可以知道,系统提供了一个IPackageInstallObserverAIDL接口,理论上我们可以直接使用这个接口启动系统的服务,然后通过调用相应得方法就可以实现。

实现原理大概说一下:首先通过反射获取系统的ServiceManager,然后通过getService方法获取PackageService,这个Service就是一个IBinder对象,接下来就可以获得IPackageManager了,用这个来调用installPackage方法。下面有一段从网上抄来的代码:

 Class clazz = Class.forName("android.os.ServiceManager");  
            Method method = clazz.getMethod("getService", String.class);  
            IBinder iBinder = (IBinder) method.invoke(null, "package");  
            IPackageManager ipm = IPackageManager.Stub.asInterface(iBinder);  
            @SuppressWarnings("deprecation")  
            VerificationParams verificationParams = new VerificationParams(null, null, null, VerificationParams.NO_UID, null);  
                        // 执行安装(方法及详细参数,可能因不同系统而异)  
            ipm.installPackage(fileName, new PackageInstallObserver(), 2, null, verificationParams, ""); 

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我这里采用的方式是直接把PackageManager源码拷贝过来,然后做一些巧妙的处理就能调用到隐藏的api,下面会说清楚是如何实现的。

第一步:拷贝源码
  • 拷贝IPackageInstallObserver.aidl 到我们的app/src/main/aidl/目录中 记住包名一定要为android.content.pm。(这个了解过AIDL原理的都知道为什么了)
  • 拷贝PackageManager到我们 app/src/main/java/目录。包名也是android.content.pm

这样基本环境就配置好了

根据以上步骤 我们可以构建一个安卓项目,自我测试一下。

下面是测试例子地址:需要的同学可以自己下载:

代码下载地址:


你可能感兴趣的:(静默安装,静默卸载)