Hi,我是小余。 本文已收录到GitHub Androider-Planet中。这儿有 Android 进阶生长常识体系,关注大众号 [小余的自习室],在成功的路上不走失!
前语
PackageManagerService(简称PKMS)是Android体系中心服务之一,和AMS,WMS,IMS并排”Android四大金刚服务“,其办理着整个Android运用的装置更新和卸载等操作。
PKMS在咱们开发中经常会碰到,了解其底层原理对咱们开发也是很有帮助的,比方包体积优化,运用发动优化等。
注:本文源码全部依据Android T
目录
0.Framework包办理结构
Android包办理首要体现在以下几个部分:
- 1.体系发动进程中PKMS对系部分统装备文件进行读取,如package.xml文件,然后对外供给app信息查询接口(IPackageManager).
- 2.供给apk/apex的装置,更新,卸载等操作api接口(IPackageInstaller),apex是谷歌供给的相似apk的体系更新模块。
- 3.运用运转进程中对体系权限的查看
Android Framework包办理结构(依据Android T)如下图:
依据Android T的Android包办理机制首要分为以下三层:
1.运用层
运用层需求获取某个装置包信息或许装置运用时,需求获取PKMS的实例,PKMS是在体系发动的时分注册到ServiceManager中。运用经过调用ContextImpl的getPackageManager接口获取PKMS的实例,实践回来的是一个ApplicationPackageManager目标,这个目标完成了PackageManager抽象接口且内部有个IPackageManager目标是长途接口IPackageManager的署理目标,经过这个目标能够拜访PKMS层的接口。
IPackageManager目标有个办法getPackageInstaller能够回来长途接口IPackageInstaller的实例目标PackageInstallerService。PackageInstallerService目标能够用于运用的装置,卸载,更新等操作。
2.PKMS服务层
和AMS,WMS相同,也是在SystemServer进程发动进程中发动的,PKMS层能够被运用层调用,回来对应的app信息。其内部有三个文件目录保存相关包办理信息。
- 1./data/system/package.list
- 2./system/etc/permissions
- 3./data/system/packages.xml
PKMS在发动的时分会从这三个目录中解析相关的XML文件,然后建立庞大的信息树,运用程序能够直接的从这个信息树种查询所需的程序包信息。下面依次来看这三个文件结构:
1.data/system/package.list
用于记载体系中一切的运用的一些基本信息,包含运用称号,uid,gid,数据寄存途径等信息 下面是8.0设备上的package.list文件结构:每个版别都迥然不同,差别不是很大。
com.google.android.youtube 10076 0 /data/user/0/com.google.android.youtube default:targetSdkVersion=26 3003
每行格式如下:
- 1.
com.google.android.youtube
//app的包名,也便是AndroidManifest.xml文件中的package=”xxx.xxx.xxx”设置的内容 - 2.
10076
//uid 假如没有在AndroidManifext.xml里运用android:sharedUserId特点指定UID, 在app装置的时分,体系会给这个app自动分配一uid,以后app运转时,就用这个UID运转 - 3.
0
//debugable app是否处于调试形式,由AndroidManifext.xml里android:debuggable指定 - 4.
/data/user/0/com.google.android.youtube
//app的数据寄存途径,一般是”/data/data/${package_name}”这样的文件夹 /data/user/0指向/data/data/ - 5.
default:targetSdkVersion=26
//默许的targetSdkVersion - 6.
3003
//app所属的user group
2./system/etc/permissions
该目录下的文件首要用于权限的办理,包含两件事:1.界说体系中包含了哪些feature,运用程序能够在AndroidManifest.xml中运用标签声明程序需求哪些feature。 2.该目录还有一个还有一个platform.xml文件,该文件为一些特别uid和gid分配一些默许权限,给uid分配权限运用标签,给gid分配权限运用标签。
3.data/system/packages.xml
文件中记载了体系的一切权限信息,体系中一切装置运用的基本信息,体系中一切shared-user信息以及运用打包时的签名信息等。packages文件有点相似注册表,比方包的称号、途径、权限等等。
下面是8.0设备上的package.xml文件结构:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
//节点version 存储了当时设备的基本信息:包含sdk版别,database版别,以及指纹等信息。
<version sdkVersion="26" databaseVersion="3" fingerprint="google/sdk_gphone_x86/generic_x86:8.0.0/OSR1.180418.024/6450276:userdebug/dev-keys" />
<version volumeUuid="primary_physical" sdkVersion="26" databaseVersion="26" fingerprint="google/sdk_gphone_x86/generic_x86:8.0.0/OSR1.180418.024/6450276:userdebug/dev-keys" />
//节点permission-trees代表了一组权限信息 :如有com.google.android.googleapps.permission.GOOGLE_AUTH:p1 或许com.google.android.googleapps.permission.GOOGLE_AUTH:p2等
<permission-trees>
<item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
</permission-trees>
//permissions:表明体系中一切的权限信息 package表明该权限归属于某个运用,没有package表明归属于体系,protection表明当时权限的等级,如normal或dangerous等。
<permissions>
<item name="com.google.android.apps.nexuslauncher.permission.READ_SETTINGS" package="com.google.android.apps.nexuslauncher" />
<item name="com.google.android.gms.auth.api.phone.permission.SEND" package="com.google.android.gms" protection="2" />
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
<item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />
<item name="android.permission.REMOTE_AUDIO_PLAYBACK" package="android" protection="2" />
//省掉一大串permission item。。。
</permissions>
//package:表明一个运用的信息 name:运用包名 codePath;代码放置的途径 nativeLibraryPath:表明apk的so库寄存途径 。。。。
<package name="com.google.android.youtube" codePath="/system/app/YouTube" nativeLibraryPath="/system/app/YouTube/lib" primaryCpuAbi="x86" publicFlags="945536581" privateFlags="0" ft="171cdd40748" it="171cdd40748" ut="171cdd40748" version="121741370" userId="10076" isOrphaned="true">
//签名信息
<sigs count="1">
<cert index="1" key="308204a830820390a003020102020900847e4f..." />
</sigs>
//权限信息
<perms>
<item name="com.google.android.c2dm.permission.RECEIVE" granted="true" flags="0" />
<item name="android.permission.USE_CREDENTIALS" granted="true" flags="0" />
<item name="com.google.android.providers.gsf.permission.READ_GSERVICES" granted="true" flags="0" />
<item name="com.google.android.youtube.permission.C2D_MESSAGE" granted="true" flags="0" />
//省掉
</perms>
//app运用的公钥信息的id。对应下面的keysets节点中的公钥信息
<proper-signing-keyset identifier="15" />
//域名验证
<domain-verification packageName="com.google.android.youtube" status="0">
<domain name="youtu.be" />
<domain name="m.youtube.com" />
<domain name="youtube.com" />
<domain name="www.youtube.com" />
</domain-verification>
</package>
//shared-user节点代表一个shareuser的特点信息。声明晰shareuser的运用的userid是固定相同的、
<shared-user name="android.uid.system" userId="1000">
<sigs count="1">
<cert index="3" />
</sigs>
<perms>
<item name="android.permission.BIND_INCALL_SERVICE" granted="true" flags="0" />
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.CONFIGURE_WIFI_DISPLAY" granted="true" flags="0" />
<item name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" granted="true" flags="0" />
<item name="android.permission.ACCESS_WIMAX_STATE" granted="true" flags="0" />
<item name="android.permission.USE_CREDENTIALS" granted="true" flags="0" />
<item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
//省掉
</perms>
</shared-user>
//keyset存储了一切的运用对应的公钥信息。
<keyset-settings version="1">
<keys>
<public-key identifier="1" value="MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ..." />
<public-key identifier="2" value="MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAK..." />
//省掉
</keys>
<keysets>
<keyset identifier="1">
<key-id identifier="1" />
</keyset>
<keyset identifier="2">
<key-id identifier="2" />
</keyset>
//省掉。。。
</keysets>
//表明最近一次运用的公钥id
<lastIssuedKeyId value="29" />
<lastIssuedKeySetId value="29" />
</keyset-settings>
</packages>
从文件中能够看出每个节点会有多个相同节点,阐明这些节点并不是仅有的。所以写入到内存中也是一个调集的形式存在。
packages文件中各节点剖析如下:
节点称号 | 节点信息 |
---|---|
version | 展示了当时设备的基本信息:包含sdk版别,database版别,以及指纹等信息。 |
permission-trees | 代表了一组权限信息 :如有com.google.android.googleapps.permission.GOOGLE_AUTH:p1 或许com.google.android.googleapps.permission.GOOGLE_AUTH:p2等 而com.google.android.googleapps.permission.GOOGLE_AUTH代表了这两种权限的调集。 |
permissions | 表明体系中一切的权限信息 package表明该权限归属于某个运用,没有package表明归属于体系,protection表明当时权限的等级,如normal或dangerous等。 |
package | 表明一个运用的在体系中的信息: name:运用包名 codePath:代码放置的途径 ,即class文件寄存的目录,假如是体系app,寄存在system分区,假如是第三方app,寄存在data分区 nativeLibraryPath:表明apk的so库寄存途径。 primaryCpuAbi:表明app以哪种abi架构运转是armabi还是armabi-v7a,x86等 ft:表明apk文件前次被更改的时刻,it:表明app第一次装置的时刻,ut:表明app前次被更新时刻,它的值好像一向和ft相同, ota或app重装之后,这儿的ft和ut可能会改动。 version:版别号:也便是versioncode信息。 userId:是为app分配的user id, 假如有运用shareUserId, 这儿呈现的是SharedUserId。sigs count:表明这个app有多少个签名信息,cert里的index表明这个app运用的证书的序号,key是app运用的证书内容的ascii码值 perms;表明当时apk申请的权限信息。 |
shared-user | 代表一个shareuser的特点信息。声明晰同一个shareuser的运用的userid是同一个。 perms表明当时shared-user具有的权限信息 |
keyset-settings |
keyset-settings块里收集了一切app签名的公钥信息,和前面介绍的package信息有关 keysets块中包含了许多keyset, 每个keyset都有一个编号用identifier表明,keyset里包含的key-id里的identifier和上面keys中public-key的identifier的值相对应。 keys块中public-key里的value值便是从apk包里的签名文件里提取出来的的公钥的值。 lastIssuedKeyId和lastIssuedKeySetId表明的是最近一次取出来的公钥所属的set编号 能够看到package.xml文件在体系发动进程中起着一个非常重要的作用,首要体现在对运用清单装备以及运用的权限把控方面。 |
PKMS中还初始化了一个PackageInstallerService服务,用于运用的装置卸载和更新等操作,终究是经过binder通讯和体系层的installd体系服务进行通讯。
3.文件体系层
除了上面说的几个的体系包装备文件。还有运用装置的文件体系,包含:第三方运用以及体系运用。 一切的体系app保存在**/system/app目录下,一切的第三方app保存在/data/app目录下。 对于非体系运用,在装置前,apk文件会被复制到一个临时文件夹下面,装置成功后会被放到/data/app**下面,且运用apk的包名为途径存储。
data/dalvik-cache目录保存着着程序执行代码, 当Android发动时,DalvikVM监视一切的程序(APK文件)和结构,并且为他们创立一个依存联系树。 DalvikVM经过这个依存联系树来为每个程序优化代码并存储在Dalvik缓存中。这样,一切程序在运转时都会运用优化过的代码。
data/data是运用的私有目录,其他运用对此是没有拜访权限的,/sdcard/Android/data属于运用的外部存储目录。
system/framework下存储了体系运转的各种jar包,framework-res.apk则存储了framework体系需求的各种资源文件。
4.Installd体系服务层
Installd体系服务首要用来运转apk的装置和卸载,dex优化等作业,而PKMS收到运用装置使命时,会把终究使命提交给Installd进行处理。
Installd进程具有root权限,而PKMS只具有体系权限。
Installd进程在6.0之前运用的是socket通讯,之后运用的是binder通讯。
有了一个包办理的全体认知进程,下面咱们再来具体剖析进程
小余会运用下面两个视点来解说PKMS在体系中的具体功用以及完成逻辑:
- 1.PKMS的发动进程
- 2.第三方运用的装置进程。
1.PKMS的发动进程:
1.1:发动进程:
PKMS在体系发动进程中的SystemServer进程的startBootstrapServices中发动
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
//创立一个Installer服务,实践执行PKMS运用装置卸载等操作的服务。后边会具体介绍
Installer installer = mSystemServiceManager.startService(Installer.class);
Pair<PackageManagerService, IPackageManager> pmsPair = PackageManagerService.main(
mSystemContext, installer, domainVerificationService,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
//省掉
}
看PackageManagerService的main办法:
public static Pair<PackageManagerService, IPackageManager> main(Context context,
Installer installer, @NonNull DomainVerificationService domainVerificationService,
boolean factoryTest, boolean onlyCore) {
/*
PackageManagerServiceInjector内存存储了PKMS运转的环境,能够了解为PKMS环境的包装类,
前期版别是都在PKMS中完成,新版别运用PackageManagerServiceInjector对要害类进行了一个包装。
其间初始化了许多要害类,如:
Settings:对apk的信息进行读取存储
PackageDexOptimizer:用于dex优化
PackageParser2:用于AndroidManifest中四大组件的解析操作
ApexManager:对APEX包的办理,apex和apk相似,仅仅其是谷歌发布的体系更新包。
...
*/
PackageManagerServiceInjector injector = new PackageManagerServiceInjector(
context, lock, installer, installLock, new PackageAbiHelperImpl(),
backgroundHandler,
SYSTEM_PARTITIONS,
(i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),
(i, pm) -> PermissionManagerService.create(context,
i.getSystemConfig().getAvailableFeatures()),
(i, pm) -> new UserManagerService(context, pm,
new UserDataPreparer(installer, installLock, context, onlyCore),
lock),
(i, pm) -> new Settings(Environment.getDataDirectory(),
RuntimePermissionsPersistence.createInstance(),
i.getPermissionManagerServiceInternal(),
domainVerificationService, backgroundHandler, lock),
(i, pm) -> AppsFilterImpl.create(i,
i.getLocalService(PackageManagerInternal.class)),
(i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
(i, pm) -> SystemConfig.getInstance(),
(i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),
i.getContext(), "*dexopt*"),
(i, pm) -> new DexManager(i.getContext(), i.getPackageDexOptimizer(),
i.getInstaller(), i.getInstallLock()),
(i, pm) -> new ArtManagerService(i.getContext(), i.getInstaller(),
i.getInstallLock()),
(i, pm) -> ApexManager.getInstance(),
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
(i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
() -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), pm.mCacheDir,
pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), null,
pm.mPackageParserCallback) /* scanningPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(),
null, pm.mPackageParserCallback) /* preparingPackageParserProducer */,
// Prepare a supplier of package parser for the staging manager to parse apex file
// during the staging installation.
(i, pm) -> new PackageInstallerService(
i.getContext(), pm, i::getScanningPackageParser),
(i, pm, cn) -> new InstantAppResolverConnection(
i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
(i, pm) -> new ModuleInfoProvider(i.getContext()),
(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
(i, pm) -> domainVerificationService,
(i, pm) -> {
HandlerThread thread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
thread.start();
return new PackageHandler(thread.getLooper(), pm);
},
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService,
(i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),
(i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
Context.BACKUP_SERVICE)),
(i, pm) -> new SharedLibrariesImpl(pm, i));
//调用PackageManagerService的结构办法,结构办法是整个发动进程的中心
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,
Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);
//这儿是用于第一次发动firstboot的时分需求装置体系白名单中的运用。
m.installAllowlistedSystemPackages();
//结构IPackageManagerImpl目标并将其add到ServiceManager中:name为package
IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
ServiceManager.addService("package", iPackageManager);
//结构IPackageManagerImpl目标并将其add到ServiceManager中:name为package_native
final PackageManagerNative pmn = new PackageManagerNative(m);
ServiceManager.addService("package_native", pmn);
//结构一个PackageManagerLocalImpl目标并将其add到本地LocalManagerRegistry中。
LocalManagerRegistry.addManager(PackageManagerLocal.class, m.new PackageManagerLocalImpl());
return Pair.create(m, iPackageManager);
}
总结下PKMS的main办法:
- 1.创立了一个PackageManagerServiceInjector目标用于PKMS的运转时环境
- 2.调用了PKMS的结构办法结构一个PKMS目标,结构办法内部是整个PKMS发动进程的中心,下面会剖析
- 3.调用installAllowlistedSystemPackages将结构办法中获取的运用信息写入xml文件
- 4.给ServiceManager增加一个name为package的IPackageManagerImpl目标,这个目标是运用层运用getSystemService办法获取到的PMS目标,后期解说运用装置会用到,很要害的一个类,打码~!!
- 5.在本地LocalManagerRegistry中注册了一个以PackageManagerLocal.class为key,以PackageManagerLocalImpl目标为值的键值对
- 6.回来一个Pair:分别对应PackageManagerService目标和iPackageManager服务目标
1.2:PKMS的结构进程
在main办法中调用了PKMS的结构办法,PKMS结构办法中首要是对某些xml文件信息进行读取,然后写入到内存中。
下面咱们就来剖析PKMS的结构办法,结构办法大致能够分为以下两个阶段:
- 阶段1:读取运用相关的文件如package.list以及package.xml等文件
- 阶段2:扫描体系中的apk并写入到PKMS内存或许文件中。
阶段1:读取运用相关的文件如package.list以及package.xml等文件
注意这儿只提取要害办法:
public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore..){
//前面省掉一大堆初始化的作业
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
//...
mFirstBoot = !mSettings.readLPw(computer,
mInjector.getUserManagerInternal().getUsers(
/* excludePartial= */ true,
/* excludeDying= */ false,
/* excludePreCreated= */ false));
}
能够看到阶段1中运用了一个mSettings字段来处理。这个字段是干嘛的呢?进入内部看看
/**
Holds information about dynamic settings.
*/
public final class Settings implements Watchable, Snappable
源码中注释很清楚:这个类是用来保存动态设置的信息,在这儿便是PKMS用来保存当时体系中的运用相关信息的。 看Settings结构办法:
Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,...){
...
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
在其结构办法中初始化了几个文件类:包含前面剖析的两个要害文件packages.xml和packages.list。 packages-backup.xml是packages.xml的备份文件,而packages-stopped在Android T中被注明已过期。 packages-backup备份文件假如存在的情况,体系发动进程中会优先加载备份文件中的packages信息到PKMS中,这是由于packages.xml文件是存在被损坏的可能性的,所以backup的优先级更高。这在后边源代码处也能够看到
好了,咱们回到PKMS的结构办法第一阶段,调用了下面两个要害办法。
要害办法1:mSettings.addSharedUserLPw
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.mAppId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.mAppId = uid;
if (mAppIds.registerExistingAppId(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}
其实便是初始化一些SharedUser的name和SharedUserSetting的一一对应联系。 如对于name为“android.uid.system”的SharedUser,其对应了SharedUserSetting pkgFlags:ApplicationInfo.FLAG_SYSTEM pkgPrivateFlags:ApplicationInfo.PRIVATE_FLAG_PRIVILEGED uid :Process.SYSTEM_UID
这个uid就对应了前面解析的packages.xml中的shared-user name=”android.uid.system” userId=”1000″。
要害办法2:mSettings.readLPw
boolean readLPw(@NonNull Computer computer, @NonNull List<UserInfo> users) {
FileInputStream str = null;
//mBackupSettingsFilename是前面剖析的结构办法创立的package_backup.xml文件
if (mBackupSettingsFilename.exists()) {
try {
str = new FileInputStream(mBackupSettingsFilename);
if (mSettingsFilename.exists()) {
mSettingsFilename.delete();
}
} catch (java.io.IOException e) {
// We'll try for the normal settings file.
}
}
mPendingPackages.clear();
mPastSignatures.clear();
mKeySetRefs.clear();
mInstallerPackages.clear();
try {
if (str == null) {
if (!mSettingsFilename.exists()) {
return false;
}
/在这之前的代码都是表明packages_backup的优先级高档packages文件
str = new FileInputStream(mSettingsFilename);
}
//运用PullParser对文件进行读取
final TypedXmlPullParser parser = Xml.resolvePullParser(str);
//循环读取packages_backup.xml或许packages.xml中的文件
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("package")) {//读取package节点
readPackageLPw(parser, users, originalFirstInstallTimes);
} else if (tagName.equals("permissions")) {//读取permissions节点
mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {//读取permission-trees节点
mPermissions.readPermissionTrees(parser);
} else if (tagName.equals("shared-user")) {//读取shared-user节点
readSharedUserLPw(parser, users);
} else if (tagName.equals("database-version")) {
// Upgrade from older XML schema
final VersionInfo internal = findOrCreateVersion(
StorageManager.UUID_PRIVATE_INTERNAL);
final VersionInfo external = findOrCreateVersion(
StorageManager.UUID_PRIMARY_PHYSICAL);
internal.databaseVersion = parser.getAttributeInt(null, "internal", 0);
external.databaseVersion = parser.getAttributeInt(null, "external", 0);
} else if (tagName.equals("keyset-settings")) {
mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs.untrackedStorage());
} else if (TAG_VERSION.equals(tagName)) {
final String volumeUuid = XmlUtils.readStringAttribute(parser,
ATTR_VOLUME_UUID);
final VersionInfo ver = findOrCreateVersion(volumeUuid);
ver.sdkVersion = parser.getAttributeInt(null, ATTR_SDK_VERSION);
ver.databaseVersion = parser.getAttributeInt(null, ATTR_DATABASE_VERSION);
ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
} else if (tagName.equals(DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) {
mDomainVerificationManager.readSettings(computer, parser);
} else if (tagName.equals(
DomainVerificationLegacySettings.TAG_DOMAIN_VERIFICATIONS_LEGACY)) {
mDomainVerificationManager.readLegacySettings(parser);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
str.close();
} catch (IOException | XmlPullParserException e) {
...
}
return true;
}
readLPw办法运用PullParser办法读取package.xml或许package_backup中的文件。
其间:
-
package节点的文件被初始化为PackageSetting目标放入到mPackages调集中。
final WatchedArrayMap<String, PackageSetting> mPackages;
-
permissions节点的文件被初始化为LegacyPermission目标放入到LegacyPermissionSettings的mPermissions调集中。
private final ArrayMap<String, LegacyPermission> mPermissions = new ArrayMap<>();
-
permission-trees节点的文件被初始化为LegacyPermission目标放入到LegacyPermissionSettings的mPermissionTrees调集中。
private final ArrayMap<String, LegacyPermission> mPermissionTrees = new ArrayMap<>();
而Settings类又持有LegacyPermissionSettings目标的引证,也就直接持有mPermissions和mPermissionTrees两个调集目标。
-
shared-user节点的文件被初始化为SharedUserSetting目标放入到mSharedUsers调集中。
final WatchedArrayMap<String, SharedUserSetting> mSharedUsers = new WatchedArrayMap<>();
.. 还有其他节点这儿不再描述,读者能够自行源码查看
**阶段1中经过读取体系装备文件信息,写入到mSettings目标中,PKMS持有mSettings目标引证,也就直接获取了一切体系装置包信息。**这一步对后边package的装置至关重要。
下面来看阶段2
阶段2:扫描装置体系中的apk,PackageParser2类担任文件解析
public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore..){
mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
mInjector.getSystemPartitions());
//初始化体系app
mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,
startTime);
//初始化非体系app
mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
}
第二阶段首要是扫码体系运用以及非体系运用并进行装置。
-
1.初始化体系app
InitAppsHelper.java public OverlayConfig initSystemApps(PackageParser2 packageParser,...){ ... //Apex是用于体系装置包的晋级。关注点1 mApexManager.scanApexPackagesTraced(packageParser, mExecutorService); //扫描System体系 关注点2 scanSystemDirs(packageParser, mExecutorService); // Parse overlay configuration files to set default enable state, mutability, and // priority of system overlays. final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>(); //查找APEX包中的apk,放入到apkInApexPreInstalledPaths调集中。这个调集元素存储需求在晋级APEX前需求预装置的运用处径。 for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) { for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) { apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath); } } final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance( consumer -> mPm.forEachPackage(mPm.snapshotComputer(), pkg -> consumer.accept(pkg, pkg.isSystem(), apkInApexPreInstalledPaths.get(pkg.getPackageName())))); if (!mIsOnlyCoreApps) { // do this first before mucking with mPackages for the "expecting better" case updateStubSystemAppsList(mStubSystemApps); } return overlayConfig; }
关注点1处触及到了“APEX”概念:
Apex
Apex是在Android10中谷歌为了处理体系晋级而提出的一个概念。和APK相似,Apex把Framework层中那些要害的东西搞成一个个的模块,然后能够独自晋级这些模块。 这些模块和就和一个一个的APK相似,便是一个压缩包,后缀名叫.apex。来看官方文档中对.apex文件格式的描述:
Apex和Apk的差异:
- apk是运用程序的载体,对运用开发者而言,能够apk办法对运刻苦能进行晋级。
- apex是体系功用的载体,对体系开发者(现在看首要是谷歌)而言,能够apex办法对体系功用进行晋级。 apex相当于对体系功用进行了更细粒度的划分,能够独立晋级这些功用,这些apex包将来就发布在谷歌的playstore上供咱们下载。
关注点2处调用了scanSystemDirs扫描体系目录下的apk文件进行装置,下面咱们要点来剖析下这个办法。
private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
//扫描system/overlay目录下面的包信息。这个操作要在扫描其他装置包时优先操作。overlay是一种资源的动态替换机制。
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
//扫描system/framework目录下的apk文件信息
scanDirTracedLI(frameworkDir, null,
mSystemParseFlags,
mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
//扫描system/app目录下的apk文件信息
scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
}
代码中能够看出scanSystemDirs首要扫描三个目录:
- system/overlay
- system/framework
- system/app
其间overlay目录下需求优先扫描装置,overlay是一种资源的动态替换机制。能够完成一些静态或许动态换肤的操作。具体能够参考这篇文章
来看运用的扫描办法scanDirTracedLI
private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
int parseFlags, int scanFlags,
PackageParser2 packageParser, ExecutorService executorService) {
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
scanFlags, packageParser, executorService);
}
//进入installPackagesFromDir
public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
int scanFlags, PackageParser2 packageParser,
ExecutorService executorService) {
final File[] files = scanDir.listFiles();
//创立一个ParallelPackageParser用于解析操作,其内部会运用线程池进行处理
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService, frameworkSplits);
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
//关注点1
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
//关注点2
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
String errorMsg = null;
if (throwable == null) {
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,null);
} catch (PackageManagerException e) {
}
}
}
}
看关注点1:parallelPackageParser.submit(file, parseFlags);
public void submit(File scanFile, int parseFlags) {
mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
try {
pr.scanFile = scanFile;
pr.parsedPackage = parsePackage(scanFile, parseFlags);
}
try {
mQueue.put(pr);
} catch (InterruptedException e) {
}
});
}
submit办法调用内部办法parsePackage,然后将回来的parsedPackage放入到mQueue中。
在关注点2处会运用take办法从mQueue中取出解析出来的Package并调用addForInitLI进行apk的装置操作。
咱们要点来看parsePackage办法,parsePackage内部又调用了mPackageParser的parsePackage,mPackageParser是一个PackageParser2目标。
PackageParser2.java
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,...){
if (useCaches && mCacher != null) {
ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
if (parsed != null) {
return parsed;
}
}
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
frameworkSplits);
return result。
}
解析前先看缓存中是否有对应Package,假如没有,则调用parsingUtils.parsePackage读取
//frameworks/base/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
List<File> frameworkSplits) {
if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
&& frameworkSplits.size() > 0
&& packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
return parseClusterPackage(input, packageFile, frameworkSplits, flags);
} else if (packageFile.isDirectory()) {
return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
} else {
return parseMonolithicPackage(input, packageFile, flags);
}
}
假如需求解析的是framework-res.apk或许是文件夹,则调用parseClusterPackage。 其他调用parseMonolithicPackage,这儿咱们假设是解析apk文件,则挑选走parseMonolithicPackage分支。
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
final ParseResult<ParsingPackage> result = parseBaseApk(input,
apkFile,
apkFile.getCanonicalPath(),
assetLoader, flags);
if (result.isError()) {
return input.error(result);
}
return input.success(result.getResult()
.setUse32BitAbi(lite.isUse32bitAbi()));
}
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, SplitAssetLoader assetLoader, int flags) {
final String apkPath = apkFile.getAbsolutePath();
final AssetManager assets;
try {
//获取BaseAssetManager
assets = assetLoader.getBaseAssetManager();
} catch (IllegalArgumentException e) {
}
//查找apk途径的是否在BaseAssetManager资源途径调集中
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
//运用XmlResourceParser对apk进行解析ANDROID_MANIFEST_FILENAME为AndroidManifest.xml文件
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
parser, flags);
...
final ParsingPackage pkg = result.getResult();
...
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
}
}
进入同名parseBaseApk办法,parseBaseApk又调用了parseBaseApkTags办法。 parseBaseApkTags中依据Manfest文件中的TAG为application调用了parseBaseApplication
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
final String pkgName = pkg.getPackageName();
int targetSdk = pkg.getTargetSdkVersion();
...
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final ParseResult result;
String tagName = parser.getName();
boolean isActivity = false;
switch (tagName) {
//解析activity节点
case "activity":
isActivity = true;
// fall-through
//解析receiver节点
case "receiver":
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
if (isActivity) {
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
} else {
hasReceiverOrder |= (activity.getOrder() != 0);
pkg.addReceiver(activity);
}
}
result = activityResult;
break;
//解析service节点
case "service":
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
hasServiceOrder |= (service.getOrder() != 0);
pkg.addService(service);
}
result = serviceResult;
break;
//解析provider节点
case "provider":
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (providerResult.isSuccess()) {
pkg.addProvider(providerResult.getResult());
}
result = providerResult;
break;
case "activity-alias":
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
parser, sUseRoundIcon, null /*defaultSplitName*/,
input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
}
result = activityResult;
break;
case "apex-system-service":
ParseResult<ParsedApexSystemService> systemServiceResult =
ParsedApexSystemServiceUtils.parseApexSystemService(res,
parser, input);
if (systemServiceResult.isSuccess()) {
ParsedApexSystemService systemService =
systemServiceResult.getResult();
pkg.addApexSystemService(systemService);
}
result = systemServiceResult;
break;
default:
result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
break;
}
}
...
return input.success(pkg);
}
终究对Manifest文件进行了完好的解析,首要是四大组件的信息。都放在ParsingPackage目标中回来。
四大组件保存办法如下
- 对Activity节点运用pkg.addActivity增加到pkg中
- 对Service运用pkg.addService(service);
- 对provider运用pkg.addProvider
public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage addActivity(ParsedActivity parsedActivity);
ParsingPackage addService(ParsedService service);
...
}
能够看到这个类是一个接口类,接口中供给了很全面的信息保存办法,由子类自行完成其保存办法。 Android体系中是从这个办法中获取:
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
mCallback是一个接口类,这样就将完成的办法委托给了mCallback完成类,最大极限对目标进行扩展,将目标创立操作供给给用户,这儿还是挺奇妙的。
终究定位到这个pkg的完成类为:PackageImpl extends ParsingPackageImpl, ParsingPackageImpl中完成了addActivity,addService等操作。
以上便是apk的manifest的解析进程。
下面回到installPackagesFromDir的关注点2处。
public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags...){
...
// Process results one by one
for (; fileCount > 0; fileCount--) {
//关注点2
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
String errorMsg = null;
if (throwable == null) {
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,null);
} catch (PackageManagerException e) {
}
}
}
}
apk四大组件信息解析出来后被放到mQueue中,并在关注点2处运用take取出来。最后调用addForInitLI办法处理。 addForInitLI办法会将前面解析到的Package信息,写入到对应的文件中并更新某些体系文件信息且对满意版别要求的运用进行更新操作。
一般来说发动进程中的app一般都是最新的,除非在进行一些ota操作时,需求更新某些体系的apk。
对第三方运用的解析进程和体系运用进程也是相似的,仅仅扫描的是/data/app下面的apk,这儿不再重复描述。关于PKMS的结构进程就讲这么多。
下面总结下整个PKMS的结构办法其实便是做了下面几件事情:
- 1.解析package.list以及package.xml,system/framework等文件信息写入到内存中。
- 2.依据1中的package name信息去加载体系中的运用,其实是加载apk的manifest文件。
- 3.将2中解析的manifest文件信息写入到PKMS中,并对满意版别要求的运用进行更新操作。后续运用的装置操作是运用installd服务进行。
接下里咱们来看运用是怎么装置到设备中的?
咱们知道PKMS中运用的装置卸载等作业是由Installer进程以及installd体系进程来完成的,注意两个词的差异。
1.3.Installer服务与installd服务
- Installer服务:
public class Installer extends SystemService {
//Installer服务发动的时分会调用onStart,并调用内部connect办法
@Override
public void onStart() {
if (mIsolated) {
mInstalld = null;
mInstalldLatch.countDown();
} else {
connect();
}
}
//connect办法
private void connect() {
//获取称号为installd的服务
IBinder binder = ServiceManager.getService("installd");
if (binder != null) {
try {
//binder被杀身后需求重连,调用connect,获取新的IInstalld
binder.linkToDeath(() -> {
Slog.w(TAG, "installd died; reconnecting");
mInstalldLatch = new CountDownLatch(1);
connect();
}, 0);
} catch (RemoteException e) {
binder = null;
}
}
```
if (binder != null) {
//给mInstalld赋值获取的binder。
IInstalld installd = IInstalld.Stub.asInterface(binder);
mInstalld = installd;
} else {
//给后台服务创立一个固定时刻重连机制。
BackgroundThread.getHandler().postDelayed(this::connect, CONNECT_RETRY_DELAY_MS);
}
}
```
}
从代码中了解到Installer仅仅java层封装,而实践干活是installd看护进程,称号为installd的体系服务。
- installd服务
installd服务是在体系发动进程中发动的
下面咱们来看installd服务发动进程,installd进程的发动是在installd.rc文件开始的
installd.rc
frameworks/native/cmds/installd/installd.rc
service installd /system/bin/installd
class main
installd进程运转在installd.cpp
frameworks/native/cmds/installd/installd.cpp
int main(const int argc, char *argv[]) {
return android::installd::installd_main(argc, argv);
}
static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
//初始化全局文件途径:包含获取data途径,app途径,root途径,lib目录等等
if (!initialize_globals()) {
SLOGE("Could not initialize globals; exiting.\n");
exit(1);
}
//初始化本地运用的文件夹途径
if (initialize_directories() < 0) {
SLOGE("Could not create directories; exiting.\n");
exit(1);
}
//发动InstalldNativeService服务
if ((ret = InstalldNativeService::start()) != android::OK) {
SLOGE("Unable to start InstalldNativeService: %d", ret);
exit(1);
}
//创立Binder通讯的线程池
IPCThreadState::self()->joinThreadPool();
```
return 0;
```
}
class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
public:
static char const* getServiceName() { return "installd"; }
}
由上面代码可知:installd服务发动进程其实是发动了一个InstalldNativeService服务并注册到ServiceManager中,所以InstalldNativeService运用的是binder的办法和其他进程进行通讯,在6.0之前运用的是socket的办法。
InstalldNativeService的服务称号是”installd”。
installd 进程具有root权限,而发动PKMS的SystemServer进程只要System权限,所以installd 能够对运用进行装置和卸载,而SystemServer进程仅仅一个中心者罢了。
最后咱们来看InstalldNativeService有哪些接口:
class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
public:
...
binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::string& packageName,
const std::string& instructionSet, int32_t dexoptNeeded,
const std::optional<std::string>& outputPath, int32_t dexFlags,
const std::string& compilerFilter, const std::optional<std::string>& uuid,
const std::optional<std::string>& classLoaderContext,
const std::optional<std::string>& seInfo, bool downgrade,
int32_t targetSdkVersion, const std::optional<std::string>& profileName,
const std::optional<std::string>& dexMetadataPath,
const std::optional<std::string>& compilationReason, bool* aidl_return);
```
binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
binder::Status rmPackageDir(const std::string& packageName, const std::string& packageDir);
binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid,
const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
binder::Status createOatDir(const std::string& packageName, const std::string& oatDir,
const std::string& instructionSet);
binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath,
const std::string& instructionSet,
const std::optional<std::string>& outputPath, int64_t* _aidl_return);
...省掉
```
};
InstalldNativeService中界说了许多关于运用Package的操作:如对dex文件优化,修改以及删去,对so文件的相关等操作,这儿只列出了部分操作。
好了,关于PKMS的发动进程就讲到这儿。
这儿总结下:
PKMS首要是在SystemServer进程发动进程中发动的,PKMS发动进程中首要做了以下事情:
-
1.会对某些装备文件进行解析扫描,放到PKMS目标内存中
-
2.会对体系中的运用包含:overlay,system,vendor,app等途径下的运用进行扫描,假如发现有版别更新,则进行运用更新操作。
-
3.初始化包办理进程中需求运用到一些环境目标等。
由于代码部分比较多,关于运用装置进程的解说会独自放到下篇。我是小余,期待你的关注,咱们下篇文章见。
参考:
一篇文章看了解 Android PackageManagerService 作业流程
Android T谷歌官方文档
深化了解安卓-了解一下 Android 10 中的 APEX
android overlay机制实践
Android 体系服务 PMS Installd 看护进程(二)
Android FileProvider介绍
ANDROID 包办理(PACKAGEMANAGERSERVICE)
android体系源码目录system/framework下各个jar包的用处
同类文章(引荐):
“一文读懂”系列:Android屏幕改写机制
Android Framework常识收拾:WindowManager体系(上)
“一文读懂”系列:Android中的硬件加速
“framework必会”系列:Android Input体系(一)事情读取机制
“framework必会”系列:Android Input体系(二)事情分发机制
“一文读懂”系列:无处不在的WMS
“一文读懂”系列:AMS是怎么动态办理进程的?
不知道怎么看源码?试试这几种办法~