PackageManagerService中的方法名中的LI、LIF、LPw、LPr的含义
注1:本文参考Android 7.1.1r13源码中的PackageManagerService的说明。
这个说明在Android6.0.1中是没有的。
注2:本文中PackageManagerService简称为PMS。
1. 方法名后缀为LI、LIF、LPw、LPr的方法
这里只列举一部分。
后缀 | 方法名 |
---|---|
LI | collectCertificatesLI() installPackageLI() scanPackageLI() scanDirLI() |
LIF | deletePackageLIF() deleteSystemPackageLIF() clearApplicationUserDataLIF() clearAppDataLIF() |
LP | verifySignaturesLP() checkPermissionTreeLP() findPermissionTreeLP() findPersistentPreferredActivityLP() |
LPw | grantPermissionsLPw() updatePermissionsLPw() enableSystemPackageLPw() setInstallerPackageNameLPw() |
LPr | getRequiredInstallerLPr() verifyPackageUpdateLPr() normalizePackageNameLPr() needsNetworkVerificationLPr() |
2. PMS中的两个锁
要想弄明白方法名中的LI、LIF、LPw、LPr的含义,需要先了解PackageManagerService内部使用的两个锁。因为LI、LIF、LPw、LPr中的L,指的是Lock,而后面跟的I和P指的是两个锁,I表示mInstallLock
同步锁。P表示mPackages
同步锁。LPw、LPr中的w表示writing,r表示reading。LIF中的F表示Freeze。
mPackages
同步锁,是指操作mPackages
时,用synchronized (mPackages) {}
保护起来。mPackages
同步锁用来保护内存已经解析的包信息和其他相关状态。mPackages
同步锁是细粒度的锁,只能短时间持有这个锁,因为争抢mPackages
锁的请求很多,短时间持有mPackages
锁,可以让其他请求等待的时间短些。
mInstallLock
同步锁,是指安装App的时候,对安装的处理要用synchronized (mInstaller) {}
保护起来。mInstallLock
同步锁,用来保护所有对installd
的访问。installd
通常包含对应用数据的繁重操作。
由于installd
是单线程的,并且installd
的操作通常很慢,所以在已经持有mPackages
同步锁的时候,千万不要再请求mInstallLock
同步锁。反之,在已经持有mInstallLock
同步锁的时候,可以去请求mPackages
同步锁。
用代码表示出来,是这样的:
注意:不允许的情况:
synchronized (mPackages) {
synchronized (mInstaller) {
// 这种情况是不允许的。因为Install的处理时间会很长,导致对mPackages锁住的时间加长,会使得其他对mPackages操作的请求处于长时间等待。
}
}
允许的情况:
synchronized (mInstaller) {
synchronized (mPackages) {
// 这种情况是允许的。因为mPackages处理完之后,其他对mPackages操作的请求可以对mPackages处理,不需要等待太久。
// 由于处理Install的时间本身很长,synchronized (mPackages)又较快,所以不会对原本长时间持有mInstaller锁的情况有大的影响。
}
}
注:关于Java同步的官方说明:https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
3. 方法名中的LI、LIF、LPw、LPr的含义
方法名 | 使用方式 |
---|---|
fooLI() | 调用fooLI(),必须先持有mInstallLock 锁。 |
fooLIF() | 调用fooLIF(),必须先持有mInstallLock 锁,并且正在被修改的包(package)必须被冻结(be frozen)。 |
fooLPr() | 调用fooLPr(),必须先持有mPackages 锁,并且只用于读操作。 |
fooLPw() | 调用fooLPw(),必须先持有mPackages 锁,并且只用于写操作。 |
举一些例子:
例1:方法名中带有LP的方法:verifySignaturesLP()
使用verifySignaturesLP()
的时候,要加上synchronized (mPackages)
或者在其他带LP的方法中使用。
synchronized (mPackages) {
//省略其他代码
verifySignaturesLP(pkgSetting, pkg);
}
例2: 方法名中带有LI的方法:installPackageLI()和installPackageTracedLI()
使用installPackageTracedLI()
的时候,加上synchronized (mInstallLock)
:
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
调用installPackageLI()
的时候,没有加上synchronized (mInstallLock)
,但是在带有LI的方法中使用,例如installPackageTracedLI()
中调用installPackageLI()
:
private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
try {
installPackageLI(args, res);
}
...
}
4. @GuardedBy
注解
在代码中还有一种@GuardedBy
注解(com.android.internal.annotations.GuardedBy
),用于标记哪些变量要用同步锁保护起来。
例1:@GuardedBy(“mPackages”)
@GuardedBy("mPackages")
private boolean mDexOptDialogShown;
上面的@GuardedBy
注解表示所有使用mDexOptDialogShown
的地方,都要用synchronized (mPackages)
保护起来,以保证并发时不会有错误。
例如,
synchronized (mPackages) {
mDexOptDialogShown = true;
}
synchronized (mPackages) {
dexOptDialogShown = mDexOptDialogShown;
}
例2:@GuardedBy(“mInstallLock”)
带@GuardedBy("mInstallLock")
注解的变量:mInstaller。如下:
@GuardedBy("mInstallLock")
final Installer mInstaller;
所以,使用mInstaller
的地方都要用synchronized (mInstallLock)
保护起来。
例如,
synchronized (mInstallLock) {
mSettings.createNewUserLI(this, mInstaller, userId);
}
synchronized (mInstallLock) {
mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
}
synchronized (mInstallLock) {
// 注意这里的info.args是InstallArgs。PMS中对InstallArgs的处理,也是用mInstallLock锁保护起来的。
info.args.doPostDeleteLI(true);
}
PMS中还有synchronized (mInstaller)
这种同步锁。不知道是笔误,还是就是这样子的。
synchronized (mInstaller) {
try {
mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
move.dataAppName, move.appId, move.seinfo, move.targetSdkVersion);
}
...
}
synchronized (mInstaller) {
for (int userId : installedUserIds) {
if (!getPackageSizeInfoLI(packageName, userId, stats)) {
freezer.close();
...
}
}
}
需要注意的是:在有的编译环境中可能并不检查@GuardedBy
注解,即标有@GuardedBy
注解的变量也可能会出现错误用法(即,没有被同步锁保护起来)。某些静态分析工具(例如FindBug)可能会检查@GuardedBy
注解,以保证相关变量被正确的使用(即,被同步锁保护起来)。
注:FindBug中检查的GuardedBy注解,是net.jcip.annotations.GuardedBy
,不是Android中的com.android.internal.annotations.GuardedBy
。
5. 附:PMS中的原始内容
/**
* Keep track of all those APKs everywhere.
* <p>
* Internally there are two important locks:
* <ul>
* <li>{@link #mPackages} is used to guard all in-memory parsed package details
* and other related state. It is a fine-grained lock that should only be held
* momentarily, as it's one of the most contended locks in the system.
* <li>{@link #mInstallLock} is used to guard all {@code installd} access, whose
* operations typically involve heavy lifting of application data on disk. Since
* {@code installd} is single-threaded, and it's operations can often be slow,
* this lock should never be acquired while already holding {@link #mPackages}.
* Conversely, it's safe to acquire {@link #mPackages} momentarily while already
* holding {@link #mInstallLock}.
* </ul>
* Many internal methods rely on the caller to hold the appropriate locks, and
* this contract is expressed through method name suffixes:
* <ul>
* <li>fooLI(): the caller must hold {@link #mInstallLock}
* <li>fooLIF(): the caller must hold {@link #mInstallLock} and the package
* being modified must be frozen
* <li>fooLPr(): the caller must hold {@link #mPackages} for reading
* <li>fooLPw(): the caller must hold {@link #mPackages} for writing
* </ul>
* <p>
* Because this class is very central to the platform's security; please run all
* CTS and unit tests whenever making modifications:
*
* <pre>
* $ runtest -c android.content.pm.PackageManagerTests frameworks-core
* $ cts-tradefed run commandAndExit cts -m AppSecurityTests
* </pre>
*/