android 安装第三方app,Android识别预装的第三方App方法实例

前言

新买一台手机,里面会有很多App,有的属于系统App,不可卸载,有的属于第三方App,厂商会预装一些常用的或者给了他们广告费的App,这些是可以卸载的。

如果要详细划分,系统App还可根据其路径不同进一步划分(如/system/app、/system/priv-app、/vendor/app等)。但对于开发者来说,手机上安装的App只分为2类:系统App和用户App,可以根据系统API区分,这里就不详细说了,简单而言存在ApplicationInfo.FLAG_SYSTEM或ApplicationInfo.FLAG_UPDATED_SYSTEM_APP flag的即为系统App,否则为用户App。

但是,利用这个方法那些预装的App也会归到用户App中,那么有没有办法知道用户App中哪些是预装的哪些是用户手动安装的呢?

在这里分享一种方法:App的安装时间是整秒的为预装的第三方App。

如果不关心为什么能用这个奇怪方法来区分预装App的话,就可以关闭这篇文章了。

之前我也一直不清楚为什么可以用这种方法,当时我猜是因为手机第一次启动的时候时间是不准确的,会是某某年1月1日,然后因为启动时会扫描各个App目录然后安装App,因此被打上这样的安装时间。但这种解释是说不通的,即便真是这样,那安装时间也不应该是整秒的。因此我决定好好找一下原因,以下是我求证的历程。

背景知识

首先介绍一些背景知识。

App的安装时间可以通过获取PackageInfo得到,其firstInstallTime属性即安装时间。

/data/system/packages.xml保存了手机上安装的App的信息,其中App的安装时间就保存在这里。我在下面截取了2个示例。

codePath="/data/app/com.tencent.mm-TSn6yG4fF7A_EaxE5OtrHQ=="

nativeLibraryPath="/data/app/com.tencent.mm-TSn6yG4fF7A_EaxE5OtrHQ==/lib"

primaryCpuAbi="armeabi" publicFlags="945307204"

privateFlags="0" ft="167702c7508" it="1676feab448"

ut="167702c8a57" version="1360" userId="10118">

...

codePath="/system/priv-app/DownloadProvider"

nativeLibraryPath="/system/priv-app/DownloadProvider/lib" publicFlags="944258629"

privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800"

ut="11e8dc5d800" version="28" sharedUserId="10006" isOrphaned="true">

...

其中it的值便是安装时间,这里是用十六进制保存的,上面这2个App的安装时间换算成十进制分别是1543770911816和1230739200000,对应的北京时间即2018-12-03 01:15:11和2009-01-01 00:00:00。 【嗯…想不到我12月3号1点多还没睡,还安装了个微信……】

系统启动时,PackageManagerService由SystemServer启动,PackageManagerService会扫描/data/app、/system/app、/system/priv-app、/vendor/app等等目录,可以理解为会把这些目录中的Apk安装一遍,PackageManagerService会结合上面提到的packages.xml把各个App解析成PackageParser.Package对象。

思路

根据上面的知识,我们可以知道,如果packages.xml已经有了某个App的信息,那么这个App的安装时间肯定就是packages.xml中记录的时间。第一次启动手机时packages.xml文件还不存在,或者新安装一个App时,packages.xml中还没有这个App的记录,也就是说,确认这个packages.xml中的firstInstallTime(即it)是如果生成的便是问题的关键。

以下基于7.0.0_r1版本代码。

通过搜索PackageManagerService,在scanPackageDirtyLI方法中有这么一段代码:

// Take care of first install / last update times.

if (currentTime != 0) {

if (pkgSetting.firstInstallTime == 0) {

pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;

} else if ((scanFlags&SCAN_UPDATE_TIME) != 0) {

pkgSetting.lastUpdateTime = currentTime;

}

} else if (pkgSetting.firstInstallTime == 0) {

// We need *something*. Take time time stamp of the file.

pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;

} else if ((policyFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {

if (scanFileTime != pkgSetting.timeStamp) {

// A package on the system image has changed; consider this

// to be an update.

pkgSetting.lastUpdateTime = scanFileTime;

}

}

其中,currentTime是scanPackageDirtyLI方法的一个参数。pkgSetting是从packages.xml中读取到的该App的信息(PackageSetting对象),如果packages.xml中不存在这个App的信息,会根据从Apk中解析到的信息创建一个PackageSetting。scanFileTime是Apk文件的最后修改时间。

可以看到存在这么几种情况:

传入的currentTime不为0,从packages.xml中读取到的firstInstallTime为0。这种情况会将firstInstallTime和lastUpdateTime均设置为传入的currentTime的值。

传入的currentTime不为0,传入的scanFlags设置了SCAN_UPDATE_TIME。这种情况会将lastUpdateTime设置为传入的currentTime的值。

传入的currentTime为0,从packages.xml中读取到的firstInstallTime为0。这种情况会将firstInstallTime和lastUpdateTime均设置为Apk的最后修改时间。

传入的currentTime为0,从packages.xml中读取到的firstInstallTime不为0,传入的policyFlags设置了PackageParser.PARSE_IS_SYSTEM_DIR,scanFileTime与packages.xml中读取到的timeStamp(packages.xml中package标签的ft)不相同。这种情况会将lastUpdateTime设置为Apk的最后修改时间。

对应到我们真实使用手机的场景,上面4种情况分别对应以下几种场景:

第一种情况:对应新安装App。currentTime为当前的时间戳,会将这个新安装的App的firstInstallTime和lastUpdateTime设置为当前时间戳。

第二种情况:对应更新App。currentTime为当前的时间戳,会将lastUpdateTime设置为当前时间戳,firstInstallTime保持不变。

第三种情况:手机启动时PackageManagerService扫描各个目录时发现了packages.xml中不存在的App(第一次启动时所有App都不在packages.xml中)。

第四种情况:系统更新等操作更新了系统分区的App,导致其文件的最后修改时间和记录的不一致了,会被认为是更新。

我们可以大胆猜测,第一次启动手机时会走第三种情况,因此系统App和预装App的安装时间是文件的最后修改时间,而这些文件的最后修改时间都是整秒的。

如何验证?

我们先看看上面那个com.android.providers.downloads的Apk文件的最后修改时间。

# stat DownloadProvider.apk

File: `DownloadProvider.apk'

Size: 504712 Blocks: 992 IO Blocks: 512regular file

Device: 10305h/66309d Inode: 1308 Links: 1

Access: (644/-rw-r--r--)Uid: ( 0/ root)Gid: ( 0/ root)

Access: 2009-01-01 00:00:00.000000000

Modify: 2009-01-01 00:00:00.000000000

Change: 2009-01-01 00:00:00.000000000

时间与packages.xml中保存的时间一致,确实是把文件的最后修改时间作为了安装时间。那么还有一个问题需要确认,传入的currentTime是0吗?

我们追溯调用链,会在PackageManagerService的构造函数中看到扫描各个目录的方法。调用scanDirTracedLI方法传入的最后一个参数0即scanPackageDirtyLI方法中的currentTime。感兴趣的还可以仔细看看PackageManagerService到底扫描了哪些目录。

File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);

scanDirTracedLI(vendorOverlayDir, mDefParseFlags

| PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR

| PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

// Find base frameworks (resource packages without code).

scanDirTracedLI(frameworkDir, mDefParseFlags

| PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR

| PackageParser.PARSE_IS_PRIVILEGED,

scanFlags | SCAN_NO_DEX, 0);

// Collected privileged system packages.

final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");

scanDirTracedLI(privilegedAppDir, mDefParseFlags

| PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR

| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

// Collect ordinary system packages.

final File systemAppDir = new File(Environment.getRootDirectory(), "app");

scanDirTracedLI(systemAppDir, mDefParseFlags

| PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all vendor packages.

File vendorAppDir = new File("/vendor/app");

try {

vendorAppDir = vendorAppDir.getCanonicalFile();

} catch (IOException e) {

// failed to look up canonical path, continue with original one

}

scanDirTracedLI(vendorAppDir, mDefParseFlags

| PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all OEM packages.

final File oemAppDir = new File(Environment.getOemDirectory(), "app");

scanDirTracedLI(oemAppDir, mDefParseFlags

| PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

...

scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags

| PackageParser.PARSE_FORWARD_LOCK,

scanFlags | SCAN_REQUIRE_KNOWN, 0);

如果感兴趣,你可以去跟一下安装App和更新App的代码,看传入的currentTime是不是当前的时间戳。

到此,我们已经证明了第一次启动手机时,系统会把文件的最后修改时间当成系统App和预装App的安装时间,而这个时间一般是类似于上面那样2009-01-01 00:00:00.000000000的整秒的时间(至于为什么是这样,那就是另一个问题了),而我们自己安装App时几乎不可能在一个整秒的时间安装,所有我们可以用安装时间是否为整秒来区分手机预装的App和用户手动安装的App。

至于区分预装App和用户手动安装的App有什么用?请发挥你的想象,比如说,一个用户的手机上只有你家一个手动安装的App或者少数几个App,那么他是想干什么好事呢?

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/433168.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汽车动力电池

1 CE SMPS 1.1 术语 DSCG:Discharge,VBUS下拉放电电阻 flyback:反激 forward:正激 LISN:Line Impedance Stabilization Network SMPS:Switching Mode Power Supplies,开关电源的变压器在整流桥之…

mysql 碎片率_计算MySQL表碎片的SQL整理

原标题:计算MySQL表碎片的SQL整理这是学习笔记的第 2111 篇文章在之前整理过一版MySQL的数据字典,整理了一圈,发现远比想象的复杂。当然整理的过程不光是知识梳理的过程,也是转化为实践场景的一个过程,通过这样一个体系…

全志A10 Bootload加载过程分析

A10的启动过程大概可分为5步:BootRom,SPL,Uboot,Kernel,RootFileSystem。本文只关注镜像的加载过程,分析RootRom->SPL->Uboot的启动流程。系统上电后,ARM处理器在复位时从地址0x000000开始…

android老 电池,为什么安卓手机不会因为电池的老化而降频呢?

前段时间,苹果手机的降频事件也是闹的沸沸扬扬,库克也为此进行了公开道歉,各位的吃瓜群众也是看的不亦乐乎,于是,也有不少的小伙伴会问:“为什么安卓手机不会因为电池的老化而降频?”今天&#…

华为笔记本软件商店_华为应用市场电脑版

华为应用市场电脑版是一款手机自带的应用商店下载中心,华为应用市场电脑版能够为用户提供新鲜、好玩的Android应用,华为应用市场电脑版能完美运行的应用和游戏,软件拥有独家“一键安装”的功能,它拥有智能搜索引擎,它也…

解决bbb无法加载uImage问题

使用sdk6,emmc的烧写都完成后,uboot启动后,无法加载uImage。 修改u-boot include/configs/am335x_evm.h的 CONFIG_EXTRA_ENV_SETTINGS 环境变量设置 将mmcdev0改为1即可 因为bbb上面,emmc是在mmc1上面,sd卡是在m…

修改spfile位置

虽然很多地方不建议这么做,可是有HA、oracle软件建在本地盘的情况下,如果spfile放在dbs下,会导致每次修改spfile都要去手动copy到备机上,这是很麻烦的一件事情,所以我把spflie放在随ha切换的存储磁盘上。 (…

android 解析错误 真机,AndroidStudio使用真机调试时出的一些bug

文章目录说明高德地图报错 - 错误代码7, key错误OPPO/VIVO手机安装时包解析错误说明此处记录一些这种情况的bug : 在用正式签名打包上线使用的时候没问题, 但是使用USB安装调试的时候会有的bug .高德地图报错 - 错误代码7, key错误场景 : 使用AndroidStudio开发,项目…

mybatis plug 只查id_Mybatis一对多/多对多查询时只查出了一条数据

问题描述:如果三表(包括了关系表)级联查询,主表和明细表的主键都是id的话,明细表的多条数据只能查询出来第一条/最后一条数据。三个表,权限表(Permission),权限组表(PermissionGroup),权限组与权限的关系表(Permission…

Javascript设置对象属性为只读

有时为了保护某些属性,让其无法被更改,我们会把他们设置为常量。 在某些语言里面,也许会用const来实现这样的功能。本文讲述如何在Javascript中实现这样的功能。 方法一: var myObject {get readOnlyProperty() { return 1024; } };alert(my…

linux下gsoap的初次使用 (c)

这两天,接到一个项目,需要在linux程序中调用远程的web应用,通过soap协议。开始上网查了下资料,发现了gsoap库这个好东东^_^。继续在网上搜索例子代码,发现基本都不可编译通过,于是便一边学习一边写了这个最…

android 5.0.1 libdvm.so,Android逆向进阶—— 脱壳的奥义(基ART模式下的dump)

本文作者:i春秋作家HAI_ZHU000 前言市面上的资料大多都是基于Dalvik模式的dump,所以这此准备搞一个ART模式下的dump。Dalvik模式是Android 4.4及其以下采用的模式,之后到了Android 5.0 之后就是ART模式,关于这两个模式的详细内容&…

python bunch制作可导入数据_Python bunch包_程序模块 - PyPI - Python中文网

bunch是一个支持属性样式访问的字典,一个la javascript。>>> b Bunch()>>> b.hello world>>> b.helloworld>>> b[hello] "!">>> b.helloworld!>>> b.foo Bunch(lolTrue)>>> b.foo.lo…

浪潮之巅 笔记

14.2 诺威格(Peter Norvig,Google研究院主任,ACM Fellow,人工智能专家)定理:当一个公司的市场占有率超过50%后,就无法再使市场占有率翻翻了。在一个市场占有主导地位的公司必须不断开拓新的财源,才能做到长…

Linux下gSOAP的使用 (c++)

1、下载gSOAP在gSOAP官网中可以找到最新的gSOAP安装包以及相关的文档gSOAP官网:http://www.cs.fsu.edu/~engelen/soap.htmlgSOAP下载地址:http://sourceforge.net/projects/gsoap2/files/ 2、安装gSOAPa、解压zip压缩包命令:unzip gsoap_2.7.…

android+3.0新加的动画,Android动画片

使用Android两年多了,工作中的动画也动能应付,自认为Android中的动画自己也能用个八九不离十,结果我在学习[Periscope点赞效果](http://www.jianshu.com/p/03fdcfd3ae9c)的时候发现动画的这些高级功能我从没用过、也没见过,静下来…

xy坐标转换度分秒_经纬度转换XY坐标软件

经纬度与XY坐标转换工具是一款非常好用的坐标转换器,能够将经纬度转换为XY坐标,对于从事地理相关工作者还是很有用的。有了它就不必去记住公式了,只需输入数值,点击一下就行了。软件功能1、度分秒和度的转换2、经纬度和xy的转换3、…

LeetCode-Scramble String

哎,难题又不会做, 思路没有什么难度, 关键是要把问题思考透彻, 要考虑的要点还是挺多的,不容易一下子都考虑到; 我开始的思路是按照树的根去递归的, 这样就必须要没有重复元素,因为需…

在线打开html文件,html是什么文件?html文件怎么打开?

html是什么?html即超文本标记语言,现在大多网页都是html的格式。而所谓的html文件是一种超文本文件,其中超文本可以是图片或音乐等非文字元素,使用很广泛。但是很多用户都不太明白html是什么文件?也不清楚html文件要如…

gsoap使用心得! (win32)

最近换了个工作环境,现在在大望路这边上班,呵,刚上班接到的任务就是熟悉gsoap!废话少说,现在开始gSoap学习!gSOAP是一个夸平台的,用于开发Web Service服务端和客户端的工具,在Window…