美文网首页
第三章:PMS(PackageManagerService )

第三章:PMS(PackageManagerService )

作者: momxmo | 来源:发表于2020-04-03 12:00 被阅读0次

简述

该服务主要管理设备上的应用程序,在开机启动SystemServer系统服务之后,PMS会扫描特定目录下的apk后缀名文件,将应用安装到系统中。这个流程主要的工作:

  • 将存放在磁盘上的静态应用程序文件进行解析,并将相关信息注册到系统中,具体就是解析应用的配置清单文件manifest.xml,将we你文件中配置的组件(Activity,Service,BroadcastRecevier,ContentProvider),权限等信息注册到PackageManagerService中。

1. PMS服务启动初始化

1.1 在framework层启动

frameworks/base/services/java/com/android/server/SystemServer.java .startBootstrapServices()方法中

  // Start the package manager.
...
        mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();
...
1.2 创建PMS并注册到SystemServer系统服务
class PackageManagerService extends IPackageManager.Stub {
 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;
    }
}

创建PMS服务完成,并注册到ServiceManager服务管理者中方便应用(Client端)调用该服务;

1.3 初始化一系列工作

构造函数

class PackageManagerService extends IPackageManager.Stub {
    ......

    public PackageManagerService(Context context, boolean factoryTest) {
        ......
    synchronized (mInstallLock) {
            synchronized (mPackages) {

                ......
//1. 用于创建data/system目录和一下xml文件,并配置相应的权限
          mSettings = new Settings(mPackages);
//添加特殊用户名称和UID并关联 例如:在app清单文件中 android:sharedUserId="android.uid.system" 表示该应用是系统进程, 相关的权限会和普通应用不一样
  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);
                ......
//2.重点:这里主要是对apk中的dex进行优化,针对dvm和art不同虚拟机进行优化生成odex文件,普通应用放在/data/app/包名/oat目录下,系统应用已经在系统编译的时候进行了优化;
      mInstaller = installer;
        mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                "*dexopt*");
        mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
                ......
//3.开始扫描本地apk进行解析安装
            File dataDir = Environment.getDataDirectory();
            mAppInstallDir = new File(dataDir, "app");
            mAppLib32InstallDir = new File(dataDir, "app-lib");
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
        ......
           File frameworkDir = new File(Environment.getRootDirectory(), "framework");
            mCacheDir = preparePackageParserCache(mIsUpgrade);

          // 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");
            // 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");
        ......
           // Collect all vendor packages.
            File vendorAppDir = new File("/vendor/app");
        ......


    }
   }   
}
}

可以看到,每次系统开机都会去扫码下面目录中的apk;在构造函数中,PackageManagerService(PMS)会扫描特定目录下的APK文件,然后调用scanDirTracedLI函数进行相关的加载工作,这些目录包括:

/system/framework
/system/app
/vendor/app
/data/app
/data/app-private

PackageManagerService 在启动时会扫描所有APK文件和Jar包,然后把它们的信息读取出来,保存在内存中,这样系统运行时就能迅速找到各种应用和组件的信息。扫描过程中如果遇到没有优化过的文件,还要执行转化工作。(Android 5.0是odex格式,Android 5.0之后是oat格式,保存在data/app/包名/oat目录下)。启动后,PackageManagerService将提供安装包的信息查询服务以及应用的安装和卸载服务。

1.3.1 认识Settings
//-----------------第一部分:构造函数
 Settings(File dataDir, Object lock) {
        mLock = lock;

        mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
//初始化mSystemDir 并创建data/system目录
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
//设置/data/system目录的权限
        FileUtils.setPermissions(mSystemDir.toString(),
                FileUtils.S_IRWXU|FileUtils.S_IRWXG
                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                -1, -1);
//初始化文件对象,用于记录apk包的相关信息
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
        mPackageListFilename = new File(mSystemDir, "packages.list");
//设置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");
    }
//------------------第二部分
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
        // 获取SharedUserSetting对象,其中mSharedUsers是ArrayMap<String, SharedUserSetting> 来存储的
        SharedUserSetting s = mSharedUsers.get(name);
        if (s != null) {
            if (s.userId == uid) {
                return s;
            }
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate shared user, keeping first: " + name);
            return null;
        }
        //如果在ArrayMap中没有找到对应的SharedUserSetting
        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;
    }

文件理解:

packages.xml文件用来记录所有安装了的apk的相关信息;
packages_backup.xml文件则是备份文件;
packages-stopped.xml记录被用户强制停止的应用的package信息
packages-stopped-backup.xml是packages-stopped.xml的备份文件;
packages.list文件记录非系统自带的apk数据信息

Settings.addSharedUserLPw函数功能:
SharedUserSetting存储uid对应名称以及包的标记,并通过ArrayMap<String, SharedUserSetting> 集合类存储归类;
举例:
在SystemUI的AndroidManifest.xml里面,如下所示

<manifestxmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.systemui"
       coreApp="true"
       android:sharedUserId="android.uid.system"
       android:process="system">

在xml中,声明了一个名为android:sharedUserId的属性,其值为"android.uid.system"。shareUserId看起来和UID有关,确实如此,它有两个作用:

  • 两个或者多个声明了同一种shareUserIds的APK可以共享彼此的数据,并且可以运行在同一个进程中。
  • 通过声明特定的shareUserId,该APK所在进程将被赋予指定的UID。例如,本例中的SystemUI声明了system的uid,运行SystemUI进程就可共享system用户所对应的权限(实际上就是将该进程的uid设置为system的uid)

除了在AndroidManifest.xml中声明shareUserId外,APK在编译时还必须使用对应的证书进行签名。
看图理解:


image.png
1.3.2 dex优化

dex的优化最终还是调用Installer.java
)这个类执行,PackageDexOptimizer对其进行了管理封装,想要了解Installer调用底层服务可以到第一章学习;

 @GuardedBy("mInstallLock")
    private int dexOptPath(PackageParser.Package pkg, String path, String isa,
            String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats) {
        ......
    //* 调用Installer.java进行AIDL进程通信通知root底层InstalldNativeService安装服务执行dex优化
            mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
                    compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo);
            ......
    }
1.3.3 解析APK文件manifest文件信息存储到PackageParser对象

scanDirTracedLI函数扫描对应的目录文件,如果是apk文件,就找到apk文件中的manifest文件,并解析信息传递到PackageParser.ParseResult中

public class PackageParser {
    ......

   private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
            IOException {
      ......
 int type;
  TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifest);
        ......
  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 (acceptedTags != null && !acceptedTags.contains(tagName)) {
                Slog.w(TAG, "Skipping unsupported element under <manifest>: "
                        + tagName + " at " + mArchiveSourcePath + " "
                        + parser.getPositionDescription());
                XmlUtils.skipCurrentTag(parser);
                continue;
            }
  if (tagName.equals(TAG_APPLICATION)) {
        ......
     } else if (tagName.equals(TAG_OVERLAY)) {
        ......
  } else if (tagName.equals(TAG_KEY_SETS)) {
        ......
 } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
        ......
   } else if (tagName.equals(TAG_PERMISSION)) {
        ......
 } else if (tagName.equals(TAG_PERMISSION_TREE)) {
        ......
       } else if (tagName.equals(TAG_USES_PERMISSION)) {
        ......
           } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
                    || tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
        ......
  } else if (tagName.equals(TAG_USES_CONFIGURATION)) {
        ......

        return pkg;
    }

    ......
}

这部分代码就是对AndroidManifest.xml文件中的application标签进行解析了,我们常用到的标签就有activity、service、receiver和provider,这里解析完成后,一层层返回,调用另一个版本的scanPackageLI函数把来解析后得到的应用程序信息保存下来。

2. 应用分类

Android 中应用可以简单地分成两大类:"系统应用"和"普通应用"

  • 1、系统应用是指位于/system/app 或者 /System/priv-app 目录下的应用。priv-app目录是从Android 4.4开始出现的目录,它存放的是一些系统底层的应用,如Settin、SystemUI等。/system/app中存放的是一些系统级别的应用,如:Phone、Contact等。在PackageManagerService 中,所谓的system 应用包括这两个目录下的应用,而所谓的private应用就是指 /priv-app 目录下的应用。
  • 2、普通应用就是用户安装的应用,位于目录/data/app下。普通应用也可以安装在SD上,但是系统应用不可以。

3. APk安装流程图解

image.png

相关文章

网友评论

      本文标题:第三章:PMS(PackageManagerService )

      本文链接:https://www.haomeiwen.com/subject/xghwuhtx.html