您的位置:首页 > 运维架构 > 网站架构

Android Fk:PKMS(1)-PackageManagerService的Binder架构及初始化

2017-07-24 13:31 465 查看

Android Fk:PKMS(1)-PackageManagerService的Binder架构及初始化

一、PKMS的概述及其Binder架构

1. PKMS的基本功能:

  PackageManagerService是Android的核心服务,负责系统中的Package的管理,应用的安装,卸载,信息查询等。

  手机平台的Android代码PKMS为了优化或者新功能更改了很多,本文主要基于Android 7.1.1_r6源码进行代码分析。

2.PKMS的架构:

  PKMS家族主要的组成,摘自邓凡平的《深入理解android卷2》第四章,该图高度概括了PackageManagerService的家族组成:



2.1 IPackageManager类

  IPackageManager是由aidl文件生成的接口类,源码中无IPackageManager.java,该文件是编译时由IPackageManager.aidl(/frameworks/base/core/java/android/content/pm/IPackageManager.aidl)生成的。

  查看整理后该类的源码,主要看实现结构,可以看到生成的IPackageManager结构如上图所示:

//out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content/pm/IPackageManager.java
public interface IPackageManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements android.content.pm.IPackageManager {
private static final java.lang.String DESCRIPTOR = "android.content.pm.IPackageManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

public static android.content.pm.IPackageManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof android.content.pm.IPackageManager))) {
return ((android.content.pm.IPackageManager) iin);
}
return new android.content.pm.IPackageManager.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {...}
case TRANSACTION_checkPackageStartable: {...}
case TRANSACTION_isPackageAvailable: {...}
....
}
...
}
...
private static class Proxy implements android.content.pm.IPackageManager
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public void checkPackageStartable(java.lang.String packageName, int userId) throws android.os.RemoteException
{   ...
mRemote.transact(Stub.TRANSACTION_checkPackageStartable, _data, _reply, 0);
}
@Override public boolean isPackageAvailable(java.lang.String packageName, int userId) throws android.os.RemoteException
{
...
mRemote.transact(Stub.TRANSACTION_isPackageAvailable, _data, _reply, 0);
...
}
}
}
...
}


2.2 PackageManagerService

  PackaManagerService是服务的实现类,继承自IPackageManager.Stub类,因此实际上它是一个Binder;

  PackageManager是个抽象类,对IPackageManager接口类中的业务函数进行了封装,Client通过Context的getPackageManager()函数获得一个PackageManager的对象pm,通过pm去调用接口公开的方法;

  举个例子:

  通过PackageManager对象调用服务方法:

//packages/apps/PackageInstaller/src/com/android/packageinstaller/utils/AppUtils.java
public static String getAppLableByPkgName(Context context , String pkgName){
String lable = "";
PackageManager pm = context.getPackageManager();
try {
ApplicationInfo info = pm.getApplicationInfo(pkgName, 0);
lable = info.loadLabel(pm).toString();
}catch (PackageManager.NameNotFoundException e){
e.printStackTrace();
}
return lable;
}


2.3 ApplicationPackageManager

   pm实际是PackageManager的子类ApplicationPackageManager类型,ApplicationPackageManager类对抽象类PackageManager中的抽象方法进行了具体实现,pm调用接口方法也是通过ApplicationPackageManager中的方法去实现。

//frameworks/base/core/java/android/app/ContextImpl.java
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
//得到PKMS的服务代理
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
//构造成一个ApplicationPackageManager类型的对象返回
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}


2.4 ApplicationPackageManager通过PKMSProxy类型的成员变量与Binder驱动通信

  ApplicationPackageManager是通过其一个IPackageManager变量mPM来和Binder驱动进行通信的,从mPM的赋值来看mPM实际上一个IPackManager.Stub.Proxy类型的对象,例如:

//frameworks/base/core/java/android/app/ApplicationPackageManager.java
@Override
public String[] getPackagesForUid(int uid) {
try {
return mPM.getPackagesForUid(uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}


  构造ApplicationPackageManager时赋值为IPackageManager类型的参数给mPm:

ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
}


  我们看新建ApplicationPackageManager实例的地方:

//frameworks/base/core/java/android/app/ActivityThread.java
try {
ii = new ApplicationPackageManager(null, getPackageManager())
.getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException( "Unable to find instrumentation info for: " + data.instrumentationName);
}


  接著看ActivityThread中的getPackageManager()方法:

//frameworks/base/core/java/android/app/ActivityThread.java
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");//得到PKMS的BinderProxy对象
sPackageManager = IPackageManager.Stub.asInterface(b);//得到PKMS的IPackageManager.Stub.Proxy对象
return sPackageManager;
}


  从之前的Java Binder分析中我们知道这里的通过IPackageManager.Stub类的asInterface()得到了PKMS的服务对象IPackageManager.Stub.Proxy(BinderProxy(BpBinder(handle))),上面提供的IPackageManager.java的代码中也有asInterface()方法的实现;

  PKMS的Binder架构如下:



  从图中可以看出通过aidl技术,Client通过PKMS的proxy去跨进程调用到Server端的Stub,底层依然是依靠Binder机制进行支撑;

  Client获取PackageManagerService的代理对象过程:

  

    


  通过一层一层的封装,Client调用PKMS的过程最终是通过获得IPackageManager.Stub.Proxy类对象进行方法调用的;

二、PKMS的启动

  PKMS是系统核心服务,由SystemServer创建启动:

public final class SystemServer {
private PackageManagerService mPackageManagerService;
private void run() {
...
startBootstrapServices();
startOtherServices();
...
}

private void startBootstrapServices() {
...
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
if (!mOnlyCore) {
boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
false);
if (!disableOtaDexopt) {
try {
OtaDexoptService.main(mSystemContext, mPackageManagerService);
} catch (Throwable e) {...} finally {...}
}
}
...
}

private void startOtherServices() {
if (!mOnlyCore) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "UpdatePackagesIfNeeded");
try {
mPackageManagerService.updatePackagesIfNeeded();
} catch (Throwable e) {...}
}

...

try {
mPackageManagerService.systemReady();//PMS ok
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
}
}


  PKMS的main函数分析:

public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
return m;
}


  我们看到PKMS的main函数主要做了一些系统属性的检查,然后构造了一个PKMS的实例,enable一些系统用户的package,然后将构造出的PKMS和PKMS的名字注册至ServiceManager中;

其中最重要的部分就是PKMS的构造函数中的操作了。

  PKMS的构造函数代码比较长,我们选取重要的几个步骤进行分析:

1. 构造Setting类

mSettings = new Settings(mPackages);
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);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);


 首先来看下Settings的构造函数:

//frameworks/base/services/core/java/com/android/server/pm/Settings.java
Settings(Object lock) {
this(Environment.getDataDirectory(), lock);
}

Settings(File dataDir, Object lock) {
mLock = lock;

mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);

mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
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");
}


  我们看到Settings的构造函数主要工作就是创建了系统文件夹,一些包管理的文件:

   packages.xml和packages-backup.xml为一组,用于描述系统所安装的Package信息,其中packages-backup.xml是packages.xml的备份

   packages-list用于描述系统中存在的所有非系统自带的apk信息及UID大于10000的apk。当这些APK有变化时,PKMS就会更新该文件;

  我们可以到手机对应目录找的到这些文件,可以pull出来看看。



1.2 添加特殊用户的名称和UID并关联

SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
...
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
if (addUserIdLPw(uid, s, name)) {
//将name和SharedUserSetting对象保存到mShareUsers的一个ArrayMap中
mSharedUsers.put(name, s);
return s;
}
return null;
}


  addSharedUserLPw函数将name和SharedUserSetting对象加到mSharedUsers的列表中,这里我们主要关心两点,一是ShareUserSetting的架构、二是ShareUserSetting的作用:

[b]1.2.1. ShareUserSetting的架构:[/b]

  在PKMS的构造函数中建了一个Settings的实例mSettings,mSettings实例中有三个成员变量:mSharedUsers,mUserIds,mOtherUserIds;

  addSharedUserLPw()函数都涉及到了这个三个成员变量,看到PKMS中创建了Settings的实例对象mSettings,addSharedUserLPw()函数是对mSettings的成员变量mShareUsers进行操作,mShareUsers是个以String name为key,ShareUserSetting对象为value的ArrayMap,SharedUserSetting中成员变量packages是一个PackageSetting类型的ArraySet;PackageSetting继承自PackageSettingBase,我们可以看到PackageSetting中保存着package的多种信息。



[b]1.2.2. SharedUserId作用:[/b]

  我们在系统应用的AndroidManifest.xml中

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.settings"
coreApp="true"
android:sharedUserId="android.uid.system"
android:versionCode="1"
android:versionName="1.0" >
</manifest>


  这里的android:shareUserId的属性即对应着SharedUserSetting中的name,上面的addSharedUserLPw函数将shareUserId name和一个int类型的UID对应了起来,UID的定义在Process.java中:

"android.uid.system"  SYSTEM_UID = 1000;
"android.uid.phone"  PHONE_UID = 1001;
"android.uid.log"  LOG_UID = 1007;
"android.uid.nfc"  NFC_UID = 1027;
"android.uid.bluetooth"  BLUETOOTH_UID = 1002;
"android.uid.shell"   SHELL_UID = 2000;


  shareUserId与UID相关,作用是:

  1.两个或多个apk或者进程声明了同一种shareUserId的APK可共享彼此的数据,并且可以运行在同一进程中(相当于进程是系统的用户,某些进程可以归为同一用户使用,相当于linux系统中的GroupId)。

  2.通过声明特定的sharedUserId,该APK所在的进程将被赋予指定的UID,将被赋予该UID特定的权限。

  小结一下,这一部分主要构造了mSetting实例,初始化了一些文件,添加了一些特殊的用户的名字和ID之间的对应关系。

  


2. 获取系统配置保存值本地变量

SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();//取出全局groupid保存到PKMS的全局变量中
mSystemPermissions = systemConfig.getSystemPermissions();//取出系统权限保存到PKMS的全局变量中
mAvailableFeatures = systemConfig.getAvailableFeatures();//取出可用的feature保存在PKMS的全局变量中


  看到这部分主要是获得SystemConfig实例,利用SystemConfig实例获取到系统配置保存到PKMS本地的全局变量中。

  先看下SystemConfig的构造函数:

SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
//从odm目录下读取sysconfig和permission目录下的文件
int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
//从oem目录下读取sysconfig和permission目录下的文件读取定制是否支持某个feature
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);
//Remove vulkan specific features
if (SystemProperties.getBoolean("persist.graphics.vulkan.disable", false)) {
removeFeature(PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL);
removeFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION);
}
}


  SystemConfig的构造函数中主要通过readPermission函数将对应目录下的xml文件中定义的各个节点读取出来保存到SystemConfig的成员变量中;

   readPermission()第一个参数是对应的目录,第二参数是从xml文件中解析内容的范围,比如对于system目录,是全部解析:ALLOW_ALL;

  我们到system/etc/permission目录下可以看到很多xml类型的配置文件:

pollux:/ # cd system/etc/permissions/
pollux:/system/etc/permissions # ls -1
...
android.hardware.bluetooth.xml
android.hardware.bluetooth_le.xml
android.hardware.camera.flash-autofocus.xml
android.hardware.camera.front.xml
...
platform.xml
...


  这些都是编译时从framework中指定位置拷贝过来的(/frameworks/native/data/etc/);

  readPermission函数调用readPermissionsFromXml方法解析xml中的各个节点,其中xml中涉及到的标签有feature、library、permission、assign-permission等,这些标签的内容都将解析出来保存至SystemConfig的对应数据结构的全局变量中以便于以后查询管理;

  feature标签用来描述设备是否支持的硬件特性;library用于指定系统库,当应用程序运行时,系统会为进程加载一些必要库,permission用于将permission与gid关联,assign-permission将system中描述的permission与uid关联等等;

  其中解析permission调用了readPermission()函数进行权限的解析:

//frameworks/base/core/java/com/android/server/SystemConfig.java
void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {

final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
//将permission标签中的Permission封装成PermissionEntry实体
final PermissionEntry perm = new PermissionEntry(name, perUser);
mPermissions.put(name, perm);//将name和实体以键值对的方式存入mPermissions全局变量中

int outerDepth = parser.getDepth();
int type;
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 ("group".equals(tagName)) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
//将groupid加入到permissionentry实体中的gid中,即permission的name,实体与groupid对应上了
int gid = Process.getGidForName(gidStr);
perm.gids = appendInt(perm.gids, gid);
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
}
}
XmlUtils.skipCurrentTag(parser);
}
}


  总结下SystemConfig()初始化时解析的xml文件节点及对应的全局变量:



小结:

  PKMS创建的SystemConfig负责解析系统的xml配置文件,保存至SystemConfig的对应数据结构的全局变量中;这部分PKMS从SystemConfig中取出GlobalGids,SystemPermissions和AvailableFeatures保存至PKMS中相应的全局变量中。

3. HandlerThread线程启动

mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
//将该handler加入到Watchdog监测中,安装应用可能会有大量的I/O操作会比较耗时
//因此这里的WATCHDOG_TIMEOUT设置为了10min,一般为60s或30s
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);


  这里启动了一个名为TAG即PackageManager的Handler线程,该线程是PKMS的工作线程,PKMS的各种操作都将利用这里的mHandler分发至该HandlerThread去分别处理:

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
class PackageHandler extends Handler {
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
void doHandleMessage(Message msg) {
switch (msg.what) {
//具体操作方法
...
}
}

}


  PKMS的功能涉及系统中所有包的管理及系统中所有组件的查询工作,工作分量相当重,因此开一个Handler线程作为PKMS的工作线程十分有必要。

4. 重要变量的赋值

   在data/下创建相应目录,保存路径到对应的全局变量

File dataDir = Environment.getDataDirectory();
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mEphemeralInstallDir = new File(dataDir, "app-ephemeral");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
mRegionalizationAppInstallDir = new File(dataDir, "app-regional");
//针对Android系统中多用户场景
sUserManager = new UserManagerService(context, this, mPackages);


  mFirstBoot变量赋值:

mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));


  mIsUpgrade变量的赋值:

mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);


  将SystemConfig中保存的权限配置信息和library信息取出放置PKMS中的全局变量mSettings和mSharedLibraries中:

// Propagate permission configuration in to package manager.
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
= systemConfig.getPermissions();
for (int i=0; i<permConfig.size(); i++) {
SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.mPermissions.get(perm.name);
if (bp == null) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
for (int i=0; i<libConfig.size(); i++) {
mSharedLibraries.put(libConfig.keyAt(i),
new SharedLibraryEntry(libConfig.valueAt(i), null));
}


5. 确保外部库sharelibrary优化dexopt

final List<String> allInstructionSets = InstructionSets.getAllInstructionSets();
final String[] dexCodeInstructionSets =
getDexCodeInstructionSets(
allInstructionSets.toArray(new String[allInstructionSets.size()]));

/**
* Ensure all external libraries have had dexopt run on them.
*/
if (mSharedLibraries.size() > 0) {
// NOTE: For now, we're compiling these system "shared libraries"
// (and framework jars) into all available architectures. It's possible
// to compile them only when we come across an app that uses them (there's
// already logic for that in scanPackageLI) but that adds some complexity.
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {
final String lib = libEntry.path;
if (lib == null) {
continue;
}

try {
// Shared libraries do not have profiles so we perform a full
// AOT compilation (if needed).
int dexoptNeeded = DexFile.getDexOptNeeded(
lib, dexCodeInstructionSet,
getCompilerFilterForReason(REASON_SHARED_APK),
false /* newProfile */);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
getCompilerFilterForReason(REASON_SHARED_APK),
StorageManager.UUID_PRIVATE_INTERNAL,
SKIP_SHARED_LIBRARY_CHECK);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
} catch (IOException | InstallerException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
}
}
}


  这里通过实例mInstaller去调用dexopt去做ShareLibrary文件的优化,mInstaller是SystemService启动PKMS时传入的Installd的代理对象,最终将由installd在底层实现,这里主要讲述初始化的流程,所以不多关注详细的优化过程,将在后面文章中进行详细描述。

6. 扫描文件apk文件夹

  下面到PKMS初始化过程中的重头戏,PKMS在开机后需要将系统中所有的package信息统计管理起来,首先扫描系统的文件夹:

// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in VENDOR_OVERLAY_DIR.
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);


  这一部分是PMKS初始化的重量级部分,从code中看到这里扫描的文件夹有

  “/vendor/overlay”,

  “framework”,

  “system/priv-app”,

  “system/app”,

  “/vendor/app”,

  “oem/app”

  扫描文件夹的操作会一步调一步最终调用到scanPackageDirtyLI()函数,在这个函数中PKMS将package中的组件管理起来从而实现系统应用的安装过程,如图:



 从本图中我们主要知道两点,第一,PackageParser将package进行彻底的解析,第二,PKMS将上面解析得到的数据统计到自身变量中用于管理;

 PackageParser的解析过程和scanPackageDirtyLI()方法中的操作是十分重要的,学习了解对日常包管理有很大的帮助,本篇不多叙述,后面将详细学习该部分内容再更新;

系统目录下的应用system目录下,每次开机都进行扫描一次,经过扫描与解析package,将所有系统应用的组件信息注册至PKMS,即系统应用的安装过程;

  平时我们push系统应用至system目录下时,需要重启才能生效也就是因为重启时做了system目录下应用的扫描解析,即push到system下对应目录下的新的apk会在开机时安装。

7.扫描三方应用目录

接下来会对三方应用的安装目录进行扫描解析:

if (!mOnlyCore) {
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mEphemeralInstallDir, mDefParseFlags
| PackageParser.PARSE_IS_EPHEMERAL,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
}


  扫描三方的apk,这里的mAppInstallDir即是”data/app/”目录,注意到这里的scanFlags或上了SCAN_REQUIRE_KNOWN,这里是为了处理一些用户手动安装的系统应用OTA升级时出现的一些问题,这里不做细节分析。

  由上面扫描系统apk目录中的分析可知,这里扫描三方应用目录,通过PackageParser的解析,最终将这些目录下的apk的信息统计至PKMS的本地变量中。

8.核心应用首次开机等情况下需要dexopt优化

   如果是首次开机或者OTA升级之后我们需要对新的应用进行dexopt优化:

if ((isFirstBoot() || isUpgrade() || VMRuntime.didPruneDalvikCache()) && !onlyCore) {
long start = System.nanoTime();
List<PackageParser.Package> coreApps = new ArrayList<>();
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg.coreApp) {
coreApps.add(pkg);
}
}
int[] stats = performDexOptUpgrade(coreApps, false,
getCompilerFilterForReason(REASON_CORE_APP));
final int elapsedTimeSeconds =
(int) TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);
MetricsLogger.histogram(mContext, "opt_coreapps_time_s", elapsedTimeSeconds);

}


  从代码中performDexOptUpgrade()跟下去我们知道最终会调用mInstaller.dexopt()的方法,而mInsatller是PKMS初始化前面步骤中由systemServer赋值过来的Installd的关联对象,用于和Installd通信,最终会调用到Insatlld中的对应优化方法,这里不做过多叙述,之后会详细学习Installd;

9.清理内存收尾

  最后又是一些重要的本地变量进行初始化赋值,PKMS初始化的操作大概已经完成,主动调用GC回收内存:

// Now after opening every single application zip, make sure they
// are all flushed.  Not really needed, but keeps things nice and
// tidy.
Runtime.getRuntime().gc();


三、总结

本文主要学习了PKMS的代码结构及其初始化的大概过程,初始化过程中重要的操作还有很多,无法一一列出,日后遇到具体问题再具体分析;

PKMS事实上是一个binder(PKMS继承自IPackageManager.Stub,而IPackageManager.Stub继承自Binder),PKMS运行在SystemServer进程,而SystemServer进程在init的时候就起了一个Binder线程去监听Binder驱动,因此PKMS通过SystemServer进程实现与Binder驱动的联动;

Client端通过获取PKMS的服务代理对象IPackageManager.Stub.Proxy,Proxy和Stub都实现了IPackageManager的接口,Client调用Proxy中的接口方法,通过Proxy中的BinderProxy对象传递经过Binder驱动调用到服务端的Binder中的方法,即Stub中的接口实现,PKMS是Stub的子类,Stub中的接口方法在子类中具体实现,如下图总结:



PKMS初始化时的主要操作:

a.PKMS中重要的本地变量初始化赋值

b.库及Apk进行dexopt优化

c.扫描apk目录,统计组件信息至PKMS中

PKMS扫描目录的目的:PKMS在扫描apk的目录时会使用PackageParser类对apk的androidManifest.xml文件进行解析,保存到Package类型的变量中,然后通过PKMS的scanPackageDirtyLI()方法将解析后的组件数据统计到PKMS的本地变量中,用于管理查询调用,当系统中任意某个apk的package发生改变时,如卸载,升级等操作都会更新package的统计数据到PKMS,PKMS正是基于拥有系统中所有Package的信息才能胜任Package管理者的角色;

注意不同目录下扫描不同,PackageParser在解析apk包的时候对于不同安装目录下的apk解析规则是不同的,其中有很多重要的细节,这也正是adb push和adb install不同方式的安装应用可能有不同效果的原因所在;

对于系统应用安装的认识,当扫描解析完系统apk的目录,将组件数据统计到PKMS管理起来,这个过程即可以看作是系统目录下apk的安装流程,即系统应用的开机安装流程;

参考博客:

Android7.0 PackageManagerService (2) PKMS构造函数的主要工作:

http://blog.csdn.net/Gaugamela/article/details/52637814

PackageManagerService的启动过程分析

http://blog.csdn.net/yuanzeyao/article/details/42215521

深入理解PackageManagerService:

http://blog.csdn.net/u013598111/article/details/49278851

PackageManagerService(Android5.1)深入分析(一)构造函数

http://blog.csdn.net/kc58236582/article/details/50498131

PackageManagerService分析

http://www.jianshu.com/p/0b0d6f684580
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: