ITKeyword,专注技术干货聚合推荐

注册 | 登录

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

TaylorPotter 分享于 2017-07-24

推荐:Android GSM驱动模块(rild)详细分析(一)基本架构及初始化

Android的RIL驱动模块, 在hardware/ril目录下,一共分rild,libril.so以及librefrence_ril.so三个部分,另有一radiooptions可供自动或手动调试使用。都依赖于in

2019阿里云全部产品优惠券(新购或升级都可以使用,强烈推荐)
领取地址https://promotion.aliyun.com/ntms/yunparter/invite.html

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的作用:

1.2.1. ShareUserSetting的架构:

  在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的多种信息。

这里写图片描述

1.2.2. SharedUserId作用:

  我们在系统应用的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();

三、总结

  1. 本文主要学习了PKMS的代码结构及其初始化的大概过程,初始化过程中重要的操作还有很多,无法一一列出,日后遇到具体问题再具体分析;
  2. PKMS事实上是一个binder(PKMS继承自IPackageManager.Stub,而IPackageManager.Stub继承自Binder),PKMS运行在SystemServer进程,而SystemServer进程在init的时候就起了一个Binder线程去监听Binder驱动,因此PKMS通过SystemServer进程实现与Binder驱动的联动;
  3. Client端通过获取PKMS的服务代理对象IPackageManager.Stub.Proxy,Proxy和Stub都实现了IPackageManager的接口,Client调用Proxy中的接口方法,通过Proxy中的BinderProxy对象传递经过Binder驱动调用到服务端的Binder中的方法,即Stub中的接口实现,PKMS是Stub的子类,Stub中的接口方法在子类中具体实现,如下图总结:
    这里写图片描述

  4. PKMS初始化时的主要操作:
    a.PKMS中重要的本地变量初始化赋值
    b.库及Apk进行dexopt优化
    c.扫描apk目录,统计组件信息至PKMS中

  5. PKMS扫描目录的目的:PKMS在扫描apk的目录时会使用PackageParser类对apk的androidManifest.xml文件进行解析,保存到Package类型的变量中,然后通过PKMS的scanPackageDirtyLI()方法将解析后的组件数据统计到PKMS的本地变量中,用于管理查询调用,当系统中任意某个apk的package发生改变时,如卸载,升级等操作都会更新package的统计数据到PKMS,PKMS正是基于拥有系统中所有Package的信息才能胜任Package管理者的角色;
  6. 注意不同目录下扫描不同,PackageParser在解析apk包的时候对于不同安装目录下的apk解析规则是不同的,其中有很多重要的细节,这也正是adb push和adb install不同方式的安装应用可能有不同效果的原因所在;
  7. 对于系统应用安装的认识,当扫描解析完系统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

Android Fk:PKMS(1)-PackageManagerService的Binder架构及初始化 一、PKMS的概述及其Binder架构 1. PKMS的基本功能:   PackageManagerService是Android的核心服务,负责系统中的Package的管

相关阅读排行


用户评论

游客

相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。