定义 此处将应用的安装时机分为:
预装
新机或刷机后第一时间安装(1天内)
其他情况
原理 可获取的信息
Android中每个app会被作为一个用户处理,拥有一个 uid
。正常情况下app的 uid
是根据安装先后递增的, uid
的范围在 android.os.Process
中定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 public static final int FIRST_APPLICATION_UID = 10000 ;public static final int LAST_APPLICATION_UID = 19999 ;
android.content.pm.ApplicationInfo
有 flags
字段,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public int flags = 0 ; public static final int FLAG_SYSTEM = 1 <<0 ;
android.content.pm.PackageInfo
中有 firstInstallTime
字段,如下:
1 2 3 4 5 public long firstInstallTime;
判断方式 实测 uid
和 firstInstallTime
的关系如下:
uid
10000
10001
…
10060
10061
…
10100
10101
firstInstallTime
t2
t1
t5
t6
t9
t10
system
true
false
true
false
false
false
uid
连续变化,从 10000 开始
firstInstallTime
在预装应用中可能出现跳变,不随 uid
连续变化,在用户自己安装的应用中则会随 uid
连续变化
flags
为 system 的 uid
没有覆盖全部的预装应用,如 10000 ~ 10061 为预装应用,但 10001 可能为非system
根据以上规律,设计以下判断方法:
1、从高 uid
往低 uid
查询,找到第一个 flags
为 system的应用 uid
,假设为 10060,同时记录其前一个(遍历的方向来说)应用的 uid
,假设为 10061
2、找到要判断应用的 uid
,假设为 x
3、如果 x <= 10060,则认为是预装应用,否则如果 x 的安装时间在 10061 之后一天之内,则认为是第一时间安装
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public static final int TIME_BUILT_IN = 1 ;public static final int TIME_FIRST_TIME = 2 ;public static final int TIME_OTHER = 3 ;public static int getInstallTimeOfPkg (String pkg, Context context) { PackageManager pm = context.getPackageManager(); try { PackageInfo toCheckPkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_CONFIGURATIONS); long toCheckPkgInstallTime = toCheckPkgInfo.firstInstallTime; long firstBuiltInInstallTime = 0 ; int toCheckPkgUid = 0 ; for (int uid = Process.LAST_APPLICATION_UID; uid >= Process.FIRST_APPLICATION_UID; uid--) { String[] pkgList = pm.getPackagesForUid(uid); if (pkgList == null ) { continue ; } String pkgName = pkgList[0 ]; if (pkg.equals(pkgName)) { toCheckPkgUid = uid; } PackageInfo pkgInfo = pm.getPackageInfo(pkgName, PackageManager.GET_CONFIGURATIONS); boolean isSystem = (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ; if (isSystem) { if (toCheckPkgUid <= uid) { return TIME_BUILT_IN; } else if (toCheckPkgInstallTime - firstBuiltInInstallTime < DateUtils.DAY_IN_MILLIS) { return TIME_FIRST_TIME; } break ; } else { firstBuiltInInstallTime = pkgInfo.firstInstallTime; } } } catch (Exception ignored) { } return TIME_OTHER; }
PS:
Process.LAST_APPLICATION_UID 值较大,为提升效率遍历 uid
的起始值可设置小一些,如从 Process.FIRST_APPLICATION_UID + 2000 开始
通过 uid
遍历安装应用的方式在多数机型上不会被认为使用了读取安装应用列表权限
部分预装应用为非system应用,如果第一个预装app flags
不为 system,则判断出错