PackageManagerService比较长,我们挑主要的内容讲,这是这个系列的第一篇博客,我们主要介绍下构造函数以及一些对象。
一、SystemServer创建PackageManagerService
先来看下其在SystemServer的创建:
- mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
- mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
- ......
- mPackageManagerService.performBootDexOpt();
- ......
- try {
- mPackageManagerService.systemReady();
- } catch (Throwable e) {
- reportWtf("making Package Manager Service ready", e);
- }
我们来看下PackageManagerService的main函数:
- public static final PackageManagerService main(Context context, Installer installer,
- boolean factoryTest, boolean onlyCore) {
- PackageManagerService m = new PackageManagerService(context, installer,
- factoryTest, onlyCore);
- ServiceManager.addService("package", m);
- return m;
- }
main函数就是new了一个PackageManagerService然后把它加入ServiceManager中。
二、PackageManagerService构造函数
2.1 Settings
下面我们主要分析PackageManagerService的构造函数:
- mSettings = new Settings(context);//新建Settings
- mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,//往Settings中添加SharedUserSetting
- ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
- ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
- ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
- ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
- ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
- mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
- ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);
我们还是挑主要的讲,在构造函数中新建了Setting类,然后又调用了addSharedUserLPw函数。
我们先看Settings类:
- Settings(Context context) {
- this(context, Environment.getDataDirectory());
- }
- Settings(Context context, File dataDir) {
- 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);
- // Deprecated: Needed for migration
- mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
- mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
- }
Settings类在data目录下创建了system目录,然后分别保存了下面文件。
1.packages.xml:记录系统中所有安装的应用的信息, 我们来看下这个文件关于一个package
- <package name="com.android.providers.media" codePath="/system/priv-app/MediaProvider" nativeLibraryPath="/system/priv-app/MediaProvider/lib" publicFlags="944291397" privateFlags="8" ft="15659d595e8" it="15659d595e8" ut="15659d595e8" version="800" sharedUserId="10006">
- <sigs count="1">
- <cert index="2" key="308204a830820390a003020102020900f2b98e6123572c4e300d06092a864886f70d0101040500308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f6964311
- </sigs>
- <perms>
- <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
- <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
- <item name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" granted="true" flags="0" />
- <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
- <item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />
- <item name="android.permission.INTERNET" granted="true" flags="0" />
- <item name="android.permission.UPDATE_DEVICE_STATS" granted="true" flags="0" />
- <item name="android.permission.ACCESS_ALL_DOWNLOADS" granted="true" flags="0" />
- <item name="android.permission.ACCESS_DOWNLOAD_MANAGER" granted="true" flags="0" />
- <item name="android.permission.MANAGE_USERS" granted="true" flags="0" />
- <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
- <item name="android.permission.ACCESS_MTP" granted="true" flags="0" />
- <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
- <item name="android.permission.CLEAR_APP_CACHE" granted="true" flags="0" />
- <item name="android.permission.CONNECTIVITY_INTERNAL" granted="true" flags="0" />
- <item name="android.permission.MODIFY_NETWORK_ACCOUNTING" granted="true" flags="0" />
- <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
- <item name="android.permission.UPDATE_APP_OPS_STATS" granted="true" flags="0" />
- </perms>
- <proper-signing-keyset identifier="4" />
- </package>
2.packages-backup.xml:上面文件的备份
3.packages-stopped.xml:被强制停止运行的应用信息
4.packages-stopped-backup.xml:上面文件的备份
5.packages.list:保存普通应用的数据目录和uid信息等
- ......
- com.android.managedprovisioning 10009 0 /data/data/com.android.managedprovisioning platform 3003
- com.android.gifviewer 10042 0 /data/data/com.android.gifviewer default none
- com.android.dreams.phototable 10054 0 /data/data/com.android.dreams.phototable default none
- com.leadcore.telassistant 1000 0 /data/data/com.leadcore.telassistant platform 3002,1023,1015,3003,3001
- com.android.noisefield 10049 0 /data/data/com.android.noisefield default none
- com.android.smspush 10064 0 /data/data/com.android.smspush default none
- com.leadcore.codescan 10029 0 /data/data/com.leadcore.codescan platform 3003
- com.android.wallpaper.livepicker 10046 0 /data/data/com.android.wallpaper.livepicker platform none
- jp.co.omronsoft.openwnn 10051 0 /data/data/jp.co.omronsoft.openwnn default none
- com.android.settings 1000 0 /data/data/com.android.settings platform 3002,1023,1015,3003,3001
- com.leadcore.logservice 1000 0 /data/data/com.leadcore.logservice platform 3002,1023,1015,3003,3001
- com.android.calculator2 10032 0 /data/data/com.android.calculator2 default none
- com.leadcore.userfeedback 10062 0 /data/data/com.leadcore.userfeedback platform 3003
- com.leadcore.filesearch 10035 0 /data/data/com.leadcore.filesearch default none
- com.iflytek.inputmethod 10041 0 /data/data/com.iflytek.inputmethod default 3002,3003,3001
- org.simalliance.openmobileapi.uiccterminal 10061 0 /data/data/org.simalliance.openmobileapi.uiccterminal platform none
- com.android.wallpaper 10045 0 /data/data/com.android.wallpaper default none
- com.android.vpndialogs 10018 0 /data/data/com.android.vpndialogs platform none
- com.android.email 10031 0 /data/data/com.android.email default 3003
- com.android.music 10048 0 /data/data/com.android.music platform 3002,1023,1015,3003
- com.android.phone 1001 0 /data/data/com.android.phone platform 3002,3003,3001
- com.android.shell 2000 0 /data/data/com.android.shell platform 3002,1023,1015,3008
- com.android.video 10063 0 /data/data/com.android.video platform 1023,1015,3003
- com.android.providers.userdictionary 10000 0 /data/data/com.android.providers.userdictionary default 3003
- com.android.location.fused 1000 0 /data/data/com.android.location.fused platform 3002,1023,1015,3003,3001
- com.android.deskclock 1000 0 /data/data/com.android.deskclock platform 3002,1023,1015,3003,3001
- com.android.systemui 10017 0 /data/data/com.android.systemui platform 3002,1023,1015,3001,3006
- ......
当Android对文件packages.xml和packages-stopped.xml写之前,会先把它们备份,如果写文件成功了,再把备份文件删除掉。如果写的时候,系统出问题重启了,重启后会读取这两个文件时,发现有备份文件,会使用备份文件的内容,因为这个时候原文件已经损坏了。
每个应用的信息会保存在PackageSetting中,而所有的PackageSetting对象会保存在Settings的成员变量mPackages中。
回到构造函数,在Settings添加了SharedUserSetting,6种系统的uid:system radio log nfc bluetooth shell。相同shareUserId的包可以运行在一个进程中。
2.2 SystemConfig
继续分析构造函数:
- SystemConfig systemConfig = SystemConfig.getInstance();
- mGlobalGids = systemConfig.getGlobalGids();
- mSystemPermissions = systemConfig.getSystemPermissions();
- mAvailableFeatures = systemConfig.getAvailableFeatures();
SystemConfig构造函数中会去读取etc下面的各个文件分析
- SystemConfig() {
- // Read configuration from system
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "sysconfig"), false);
- // Read configuration from the old permissions dir
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "permissions"), false);
- // Only read features from OEM config
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "sysconfig"), true);
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "permissions"), true);
- }
分析完了后,会保存在各个成员变量中。
我们看下system/etc/permissions/platform.xml的一部分内容
- ......
- <permission name="android.permission.MANAGE_VOICE_KEYPHRASES">
- <group gid="audio" />
- </permission>
- <permission name="android.permission.ACCESS_FM_RADIO" >
- <group gid="media" />
- </permission>
- <!-- ================================================================== -->
- <!-- ================================================================== -->
- <!-- ================================================================== -->
- <!-- The following tags are assigning high-level permissions to specific
- user IDs. These are used to allow specific core system users to
- perform the given operations with the higher-level framework. For
- example, we give a wide variety of permissions to the shell user
- since that is the user the adb shell runs under and developers and
- others should have a fairly open environment in which to
- interact with the system. -->
- <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
- <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
- <assign-permission name="android.permission.WAKE_LOCK" uid="media" />
- <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
- <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
- <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
- ......
继续分析,会创建一个消息线程,会创建一些目录
- mHandlerThread = new ServiceThread(TAG,
- Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
- mHandlerThread.start();
- mHandler = new PackageHandler(mHandlerThread.getLooper());
- Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
- File dataDir = Environment.getDataDirectory();//存放应用数据目录
- mAppDataDir = new File(dataDir, "data");
- mAppInstallDir = new File(dataDir, "app");//放应用
- mAppLib32InstallDir = new File(dataDir, "app-lib");//native库
- mAsecInternalPath = new File(dataDir, "app-asec").getPath();
- mUserAppDataDir = new File(dataDir, "user");//存放用户数据
- mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
前面systemconfig解析etc下的文件,然后将Permissions放在mSettings的mPermission,以及共享库放在mSharedLibraries中
- 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.gids = appendInts(bp.gids, perm.gids);
- }
- }
- 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));
- }
2.3 readLPw函数
继续分析,调用了Settings的readLPw函数。
- mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
- mSdkVersion, mOnlyCore);
我们来详细分析,先会去看packages-backup.xml有没有,有这个文件说明在写packages.xml的时候系统出问题了,所以在系统启动的时候就要读备份的想xml文件内容。如果没有这个备份文件再去看packages.xml, 然后再去解析xml文件,把解析出来的内容封装在各个对象中保存在mSettings中各个变量中。
- boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,
- boolean onlyCore) {
- FileInputStream str = null;
- if (mBackupSettingsFilename.exists()) {//是否有备份文件
- try {
- str = new FileInputStream(mBackupSettingsFilename);
- mReadMessages.append("Reading from backup settings file\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "Need to read from backup settings file");
- if (mSettingsFilename.exists()) {
- // If both the backup and settings file exist, we
- // ignore the settings since it might have been
- // corrupted.
- Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
- + mSettingsFilename);
- mSettingsFilename.delete();
- }
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
- }
- mPendingPackages.clear();
- mPastSignatures.clear();
- try {
- if (str == null) {//这里为空,代表没有备份文件,就看packages.xml文件
- if (!mSettingsFilename.exists()) {
- mReadMessages.append("No settings file found\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "No settings file; creating initial state");
- mInternalSdkPlatform = mExternalSdkPlatform = sdkVersion;
- mFingerprint = Build.FINGERPRINT;
- return false;
- }
- str = new FileInputStream(mSettingsFilename);
- }
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(str, null);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
- if (type != XmlPullParser.START_TAG) {
- mReadMessages.append("No start tag found in settings file\n");
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "No start tag found in package manager settings");
- Slog.wtf(PackageManagerService.TAG,
- "No start tag found in package manager settings");
- return false;
- }
- int outerDepth = parser.getDepth();
- 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")) {
- readPackageLPw(parser);//解析packages.xml中的各内容
- } else if (tagName.equals("permissions")) {
- readPermissionsLPw(mPermissions, parser);
- } else if (tagName.equals("permission-trees")) {
- readPermissionsLPw(mPermissionTrees, parser);
- } else if (tagName.equals("shared-user")) {
- readSharedUserLPw(parser);
- } else if (tagName.equals("preferred-packages")) {
- // no longer used.
- } else if (tagName.equals("preferred-activities")) {
- // Upgrading from old single-user implementation;
- // these are the preferred activities for user 0.
- readPreferredActivitiesLPw(parser, 0);
- } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
- // TODO: check whether this is okay! as it is very
- // similar to how preferred-activities are treated
- readPersistentPreferredActivitiesLPw(parser, 0);
- } else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
- // TODO: check whether this is okay! as it is very
- // similar to how preferred-activities are treated
- readCrossProfileIntentFiltersLPw(parser, 0);
- } else if (tagName.equals("updated-package")) {
- readDisabledSysPackageLPw(parser);
- } else if (tagName.equals("cleaning-package")) {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- String userStr = parser.getAttributeValue(null, ATTR_USER);
- String codeStr = parser.getAttributeValue(null, ATTR_CODE);
- if (name != null) {
- int userId = 0;
- boolean andCode = true;
- try {
- if (userStr != null) {
- userId = Integer.parseInt(userStr);
- }
- } catch (NumberFormatException e) {
- }
- if (codeStr != null) {
- andCode = Boolean.parseBoolean(codeStr);
- }
- addPackageToCleanLPw(new PackageCleanItem(userId, name, andCode));
- }
- } else if (tagName.equals("renamed-package")) {
- String nname = parser.getAttributeValue(null, "new");
- String oname = parser.getAttributeValue(null, "old");
- if (nname != null && oname != null) {
- mRenamedPackages.put(nname, oname);
- }
- } else if (tagName.equals("last-platform-version")) {
- mInternalSdkPlatform = mExternalSdkPlatform = 0;
- try {
- String internal = parser.getAttributeValue(null, "internal");
- if (internal != null) {
- mInternalSdkPlatform = Integer.parseInt(internal);
- }
- String external = parser.getAttributeValue(null, "external");
- if (external != null) {
- mExternalSdkPlatform = Integer.parseInt(external);
- }
- } catch (NumberFormatException e) {
- }
- mFingerprint = parser.getAttributeValue(null, "fingerprint");
- } else if (tagName.equals("database-version")) {
- mInternalDatabaseVersion = mExternalDatabaseVersion = 0;
- try {
- String internalDbVersionString = parser.getAttributeValue(null, "internal");
- if (internalDbVersionString != null) {
- mInternalDatabaseVersion = Integer.parseInt(internalDbVersionString);
- }
- String externalDbVersionString = parser.getAttributeValue(null, "external");
- if (externalDbVersionString != null) {
- mExternalDatabaseVersion = Integer.parseInt(externalDbVersionString);
- }
- } catch (NumberFormatException ignored) {
- }
- } else if (tagName.equals("verifier")) {
- final String deviceIdentity = parser.getAttributeValue(null, "device");
- try {
- mVerifierDeviceIdentity = VerifierDev
- ......
2.4 扫描文件(转化格式,获取文件信息)
下面开始优化dex文件变成oat文件,记录开始扫描系统的时间。
- long startTime = SystemClock.uptimeMillis();//记录开始扫描时间
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
- startTime);
- // Set flag to monitor and not change apk file paths when
- // scanning install directories.
- final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
- final ArraySet<String> alreadyDexOpted = new ArraySet<String>();//已经优化的文件集合
- /**
- * Add everything in the in the boot class path to the
- * list of process files because dexopt will have been run
- * if necessary during zygote startup.
- */
- final String bootClassPath = System.getenv("BOOTCLASSPATH");
- final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
- if (bootClassPath != null) {
- String[] bootClassPathElements = splitString(bootClassPath, ':');
- for (String element : bootClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
- Slog.w(TAG, "No BOOTCLASSPATH found!");
- }
- if (systemServerClassPath != null) {
- String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
- for (String element : systemServerClassPathElements) {
- alreadyDexOpted.add(element);
- }
- } else {
- Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
- }
下面扫描动态库和framework下的文件执行dex到odex的转换
- */
- 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 {
- byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null,
- dexCodeInstructionSet,
- false);
- if (dexoptRequired != DexFile.UP_TO_DATE) {
- alreadyDexOpted.add(lib);
- // The list of "shared libraries" we have at this point is
- if (dexoptRequired == DexFile.DEXOPT_NEEDED) {
- mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);//到intalld执行
- } else {
- mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);//art虚拟机执行oat
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Library not found: " + lib);
- } catch (IOException e) {
- Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
- + e.getMessage());
- }
- }
- }
- }
- File frameworkDir = new File(Environment.getRootDirectory(), "framework");//framework目录
- // Gross hack for now: we know this file doesn't contain any
- // code, so don't dexopt it to avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");
- // Gross hack for now: we know this file is only part of
- // the boot class path for art, so don't dexopt it to
- // avoid the resulting log spew.
- alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
- /**
- * And there are a number of commands implemented in Java, which
- * we currently need to do the dexopt on so that they can be
- * run from a non-root shell.
- */
- String[] frameworkFiles = frameworkDir.list();
- if (frameworkFiles != null) {
- // TODO: We could compile these only for the most preferred ABI. We should
- // first double check that the dex files for these commands are not referenced
- // by other system apps.
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- for (int i=0; i<frameworkFiles.length; i++) {
- File libPath = new File(frameworkDir, frameworkFiles[i]);
- String path = libPath.getPath();
- // Skip the file if we already did it.
- if (alreadyDexOpted.contains(path)) {
- continue;
- }
- // Skip the file if it is not a type we want to dexopt.
- if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
- continue;
- }
- try {
- byte dexoptRequired = DexFile.isDexOptNeededInternal(path, null,
- dexCodeInstructionSet,
- false);
- if (dexoptRequired == DexFile.DEXOPT_NEEDED) {
- mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);
- } else if (dexoptRequired == DexFile.PATCHOAT_NEEDED) {
- mInstaller.patchoat(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Jar not found: " + path);
- } catch (IOException e) {
- Slog.w(TAG, "Exception reading jar: " + path, e);
- }
- }
- }
扫描各个目录下的文件信息,保存在PMS中。
- scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
- // Find base frameworks (resource packages without code).
- scanDirLI(frameworkDir, 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");
- scanDirLI(privilegedAppDir, 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");
- scanDirLI(systemAppDir, 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
- }
- scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
- // Collect all OEM packages.
- final File oemAppDir = new File(Environment.getOemDirectory(), "app");
- scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
还有一些,扫描并删除未成功安装的apk包,删除临时文件,升级应用的一些处理,更新应用动态库路径。
- ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
- //clean up list
- for(int i = 0; i < deletePkgsList.size(); i++) {
- //clean up here
- cleanupInstallFailedPackage(deletePkgsList.get(i));//删除未成功安装的apk包
- }
- //delete tmp files
- deleteTempPackageFiles();//删除临时文件
- // Remove any shared userIDs that have no associated packages
- mSettings.pruneSharedUsersLPw();
- if (!mOnlyCore) {//开始处理非系统应用
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
- SystemClock.uptimeMillis());
- scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
- scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanFlags | SCAN_REQUIRE_KNOWN, 0);
- .....
最后有一个扫描时间结束的log。
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
- SystemClock.uptimeMillis());
- Slog.i(TAG, "Time to scan packages: "
- + ((SystemClock.uptimeMillis()-startTime)/1000f)
- + " seconds");
2.5 赋予权限,写packages.xml
继续分析
- final boolean regrantPermissions = mSettings.mInternalSdkPlatform
- != mSdkVersion;//因为sdk版本变化,permission的定义也改变了,需要重新赋予应用权限
- if (regrantPermissions) Slog.i(TAG, "Platform changed from "
- + mSettings.mInternalSdkPlatform + " to " + mSdkVersion
- + "; regranting permissions for internal storage");
- mSettings.mInternalSdkPlatform = mSdkVersion;
- updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
- | (regrantPermissions//是否要重新赋权限
- ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
- : 0));
- // If this is the first boot, and it is a normal boot, then
- // we need to initialize the default preferred apps.
- if (!mRestoredSettings && !onlyCore) {
- mSettings.readDefaultPreferredAppsLPw(this, 0);
- }
- // If this is first boot after an OTA, and a normal boot, then
- // we need to clear code cache directories.
- mIsUpgrade = !Build.FINGERPRINT.equals(mSettings.mFingerprint);
- if (mIsUpgrade && !onlyCore) {
- Slog.i(TAG, "Build fingerprint changed; clearing code caches");
- for (String pkgName : mSettings.mPackages.keySet()) {
- deleteCodeCacheDirsLI(pkgName);//如果是执行OTA后第一次启动,清除cache
- }
- mSettings.mFingerprint = Build.FINGERPRINT;
- }
- // All the changes are done during package scanning.
- mSettings.updateInternalDatabaseVersion();//更新数据库
- // can downgrade to reader
- mSettings.writeLPr();//把mSettings写入packages.xml
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
- SystemClock.uptimeMillis());
- mRequiredVerifierPackage = getRequiredVerifierLPr();
- } // synchronized (mPackages)
- } // synchronized (mInstallLock)
- mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
- // 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();//启动垃圾回收
- private void updatePermissionsLPw(String changingPkg,
- PackageParser.Package pkgInfo, int flags) {
- // Make sure there are no dangling permission trees.
- Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
- if (bp.packageSetting == null) {
- // We may not yet have parsed the package, so just see if
- // we still know about its settings.
- bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
- }
- if (bp.packageSetting == null) {
- Slog.w(TAG, "Removing dangling permission tree: " + bp.name
- + " from package " + bp.sourcePackage);
- it.remove();
- } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
- Slog.i(TAG, "Removing old permission tree: " + bp.name
- + " from package " + bp.sourcePackage);
- flags |= UPDATE_PERMISSIONS_ALL;
- it.remove();
- }
- }
- }
- // Make sure all dynamic permissions have been assigned to a package,
- // and make sure there are no dangling permissions.
- it = mSettings.mPermissions.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
- if (bp.type == BasePermission.TYPE_DYNAMIC) {
- if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
- + bp.name + " pkg=" + bp.sourcePackage
- + " info=" + bp.pendingInfo);
- if (bp.packageSetting == null && bp.pendingInfo != null) {
- final BasePermission tree = findPermissionTreeLP(bp.name);
- if (tree != null && tree.perm != null) {
- bp.packageSetting = tree.packageSetting;
- bp.perm = new PackageParser.Permission(tree.perm.owner,
- new PermissionInfo(bp.pendingInfo));
- bp.perm.info.packageName = tree.perm.info.packageName;
- bp.perm.info.name = bp.name;
- bp.uid = tree.uid;
- }
- }
- }
- if (bp.packageSetting == null) {
- // We may not yet have parsed the package, so just see if
- // we still know about its settings.
- bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
- }
- if (bp.packageSetting == null) {
- Slog.w(TAG, "Removing dangling permission: " + bp.name
- + " from package " + bp.sourcePackage);
- it.remove();
- } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
- Slog.i(TAG, "Removing old permission: " + bp.name
- + " from package " + bp.sourcePackage);
- flags |= UPDATE_PERMISSIONS_ALL;
- it.remove();
- }
- }
- }
- // Now update the permissions for all packages, in particular
- // replace the granted permissions of the system packages.
- if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {//flags为UPDATE_PERMISSIONS_ALL
- for (PackageParser.Package pkg : mPackages.values()) {
- if (pkg != pkgInfo) {
- grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
- changingPkg);
- }
- }
- }
- if (pkgInfo != null) {//传进来的pkgInfo为null
- grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
- }
- }
我们再来看看grantPermissionsLPw函数
- private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
- String packageOfInterest) {
- final PackageSetting ps = (PackageSetting) pkg.mExtras;
- if (ps == null) {
- return;
- }
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- ArraySet<String> origPermissions = gp.grantedPermissions;
- boolean changedPermission = false;
- if (replace) {
- ps.permissionsFixed = false;
- if (gp == ps) {
- origPermissions = new ArraySet<String>(gp.grantedPermissions);
- gp.grantedPermissions.clear();
- gp.gids = mGlobalGids;
- }
- }
- if (gp.gids == null) {
- gp.gids = mGlobalGids;//把gid的群组赋值
- }
- final int N = pkg.requestedPermissions.size();
- for (int i=0; i<N; i++) {
- final String name = pkg.requestedPermissions.get(i);
- final boolean required = pkg.requestedPermissionsRequired.get(i);
- final BasePermission bp = mSettings.mPermissions.get(name);
- if (DEBUG_INSTALL) {
- if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
- }
- }
- if (bp == null || bp.packageSetting == null) {
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Unknown permission " + name
- + " in package " + pkg.packageName);
- }
- continue;
- }
- final String perm = bp.name;
- boolean allowed;
- boolean allowedSig = false;
- if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- // Keep track of app op permissions.
- ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
- if (pkgs == null) {
- pkgs = new ArraySet<>();
- mAppOpPermissionPackages.put(bp.name, pkgs);
- }
- pkgs.add(pkg.packageName);
- }
- final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- if (level == PermissionInfo.PROTECTION_NORMAL
- || level == PermissionInfo.PROTECTION_DANGEROUS) {
- // We grant a normal or dangerous permission if any of the following
- // are true:
- // 1) The permission is required
- // 2) The permission is optional, but was granted in the past
- // 3) The permission is optional, but was requested by an
- // app in /system (not /data)
- //
- // Otherwise, reject the permission.
- allowed = (required || origPermissions.contains(perm)
- || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
- } else if (bp.packageSetting == null) {
- // This permission is invalid; skip it.
- allowed = false;
- } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
- allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
- if (allowed) {
- allowedSig = true;
- }
- } else {
- allowed = false;
- }
- if (DEBUG_INSTALL) {
- if (gp != ps) {
- Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
- }
- }
- if (allowed) {
- if (!isSystemApp(ps) && ps.permissionsFixed) {
- // If this is an existing, non-system package, then
- // we can't add any new permissions to it.
- if (!allowedSig && !gp.grantedPermissions.contains(perm)) {
- // Except... if this is a permission that was added
- // to the platform (note: need to only do this when
- // updating the platform).
- allowed = isNewPlatformPermissionForPackage(perm, pkg);
- }
- }
- if (allowed) {
- if (!gp.grantedPermissions.contains(perm)) {
- changedPermission = true;
- gp.grantedPermissions.add(perm);
- gp.gids = appendInts(gp.gids, bp.gids);
- } else if (!ps.haveGids) {
- gp.gids = appendInts(gp.gids, bp.gids);
- }
- } else {
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " because it was previously installed without");
- }
- }
- } else {
- if (gp.grantedPermissions.remove(perm)) {
- changedPermission = true;
- gp.gids = removeInts(gp.gids, bp.gids);
- Slog.i(TAG, "Un-granting permission " + perm
- + " from package " + pkg.packageName
- + " (protectionLevel=" + bp.protectionLevel
- + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
- + ")");
- } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
- // Don't print warning for app op permissions, since it is fine for them
- // not to be granted, there is a UI for the user to decide.
- if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
- Slog.w(TAG, "Not granting permission " + perm
- + " to package " + pkg.packageName
- + " (protectionLevel=" + bp.protectionLevel
- + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
- + ")");
- }
- }
- }
- }
- if ((changedPermission || replace) && !ps.permissionsFixed &&
- !isSystemApp(ps) || isUpdatedSystemApp(ps)){
- // This is the first that we have heard about this package, so the
- // permissions we have now selected are fixed until explicitly
- // changed.
- ps.permissionsFixed = true;
- }
- ps.haveGids = true;
- }
PMS的构造函数先读取保存在packages.xml中的内容保存在mSettings中,然后扫描设备中几个应用目录下的应用文件,并把扫描结果保存在PMS的mPackages成员变量中,通过对比上一次扫描的结果就是mSettings内容是否有被升级包覆盖的系统应用,如果有从mPackages去除。这样mPackages和mSettings的记录一致了,最后将本次扫描内容写到packages.xml文件中。
PackageManagerService的构造函数中,调用了SystemConfig的getSystemPermissions方法来获取系统的permission列表。
下面我们就分析下如何从系统文件中获取permission。
一、分析构造SystemConfig构造函数
我们先来分析下SystemConfig类的构造函数
- SystemConfig() {
- // Read configuration from system
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "sysconfig"), false);
- // Read configuration from the old permissions dir
- readPermissions(Environment.buildPath(
- Environment.getRootDirectory(), "etc", "permissions"), false);
- // Only read features from OEM config
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "sysconfig"), true);
- readPermissions(Environment.buildPath(
- Environment.getOemDirectory(), "etc", "permissions"), true);
- }
Environment.getRootDirectory方法代表的是system目录,我们再来看看Environment.buildPath方法
- public static File buildPath(File base, String... segments) {
- File cur = base;
- for (String segment : segments) {
- if (cur == null) {
- cur = new File(segment);
- } else {
- cur = new File(cur, segment);
- }
- }
- return cur;
- }
这个方法其实就是一层一层建文件,因此第一个文件就是system/etc/sysconfig 而第二个就是system/etc/permissions下面两个oem的就一样了。而我们手机中system/etc/sysconfig 这个目录是不存在的,因此只需分析permissions目录了。
二、xml文件
我们先来看Android.hardware.wifi.xml,就是一个feature,而且大部分的文件都是这样的,除了platform.xml
- <permissions>
- <feature name="android.hardware.wifi" />
- </permissions>
下面就手机我们看下system/etc/permissions下的platform.xml文件
- <permissions>
- <!-- ================================================================== -->
- <!-- ================================================================== -->
- <!-- ================================================================== -->
- <!-- The following tags are associating low-level group IDs with
- permission names. By specifying such a mapping, you are saying
- that any application process granted the given permission will
- also be running with the given group ID attached to its process,
- so it can perform any filesystem (read, write, execute) operations
- allowed for that group. -->
- <permission name="android.permission.BLUETOOTH_ADMIN" >
- <group gid="net_bt_admin" />
- </permission>
- <permission name="android.permission.BLUETOOTH" >
- <group gid="net_bt" />
- </permission>
- <permission name="android.permission.BLUETOOTH_STACK" >
- <group gid="net_bt_stack" />
- </permission>
- <permission name="android.permission.NET_TUNNELING" >
- <group gid="vpn" />
- </permission>
- <permission name="android.permission.INTERNET" >
- <group gid="inet" />
- </permission>
- <permission name="android.permission.READ_LOGS" >
- <group gid="log" />
- </permission>
- <permission name="android.permission.WRITE_MEDIA_STORAGE" >
- <group gid="media_rw" />
- <group gid="sdcard_rw" />
- </permission>
- <permission name="android.permission.ACCESS_MTP" >
- <group gid="mtp" />
- </permission>
- <permission name="android.permission.NET_ADMIN" >
- <group gid="net_admin" />
- </permission>
- <!-- The group that /cache belongs to, linked to the permission
- set on the applications that can access /cache -->
- <permission name="android.permission.ACCESS_CACHE_FILESYSTEM" >
- <group gid="cache" />
- </permission>
- <!-- RW permissions to any system resources owned by group 'diag'.
- This is for carrier and manufacture diagnostics tools that must be
- installable from the framework. Be careful. -->
- <permission name="android.permission.DIAGNOSTIC" >
- <group gid="input" />
- <group gid="diag" />
- </permission>
- <!-- Group that can read detailed network usage statistics -->
- <permission name="android.permission.READ_NETWORK_USAGE_HISTORY">
- <group gid="net_bw_stats" />
- </permission>
- <!-- Group that can modify how network statistics are accounted -->
- <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING">
- <group gid="net_bw_acct" />
- </permission>
- <permission name="android.permission.LOOP_RADIO" >
- <group gid="loop_radio" />
- </permission>
- <!-- Hotword training apps sometimes need a GID to talk with low-level
- hardware; give them audio for now until full HAL support is added. -->
- <permission name="android.permission.MANAGE_VOICE_KEYPHRASES">
- <group gid="audio" />
- </permission>
- <permission name="android.permission.ACCESS_FM_RADIO" >
- <group gid="media" />
- </permission>
- <!-- ================================================================== -->
- <!-- ================================================================== -->
- <!-- ================================================================== -->
- <!-- The following tags are assigning high-level permissions to specific
- user IDs. These are used to allow specific core system users to
- perform the given operations with the higher-level framework. For
- example, we give a wide variety of permissions to the shell user
- since that is the user the adb shell runs under and developers and
- others should have a fairly open environment in which to
- interact with the system. -->
- <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
- <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
- <assign-permission name="android.permission.WAKE_LOCK" uid="media" />
- <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
- <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
- <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
- <!-- This is a list of all the libraries available for application
- code to link against. -->
- <library name="android.test.runner"
- file="/system/framework/android.test.runner.jar" />
- <library name="javax.obex"
- file="/system/framework/javax.obex.jar" />
- <library name="org.apache.http.legacy"
- file="/system/framework/org.apache.http.legacy.jar" />
- <!-- These are the standard packages that are white-listed to always have internet
- access while in power save mode, even if they aren't in the foreground. -->
- <allow-in-power-save-except-idle package="com.android.providers.downloads" />
- </permissions>
platform.xml主要由3块组成:
1. <permission>把属性name中的字符串表示的权限(permission)赋予<group>标签中的属性gid中的用户组
2.<assign-permission>把属性name中的字符串表示的权限(permission)赋予<group>标签中的属性uid中的用户
3.<library>表示除了framework中的动态库以外,系统将为应用自动加载的动态库。
而解析出来这些标签又放在哪呢?
1.标签<permission>中的属性name字符串和<group>标签中的gid都放到了变量mSettings的mPermissions中
2.所有的gid放在mGlobalGids中
3.标签<assign-permission>的内容放到PackageManagerService的成员变量mSystemPermission中。
mSystemPermission是放在下面这样一个数据结构中
- final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
- ArraySet<String> perms = mSystemPermissions.get(uid);
- if (perms == null) {
- perms = new ArraySet<String>();
- mSystemPermissions.put(uid, perms);
- }
- perms.add(perm);
4.<library>动态库放在mSharedLibraries
5.<feature>放在mAvailableFeatures中。
Android的很多功能是通过所谓的permission字符串来保护的,应用如果要使用某项权限,就要在AndroidManifest.xml显示的加上permission字符串。但是有些功能只能由特定用户组的应用使用,在platform.xml定义的就是这种限制规则。
下面我们再来看看核心函数readPermissions():
- void readPermissions(File libraryDir, boolean onlyFeatures) {
- // Read permissions from given directory.
- if (!libraryDir.exists() || !libraryDir.isDirectory()) {// 不是一个目录或者不存在
- if (!onlyFeatures) {
- Slog.w(TAG, "No directory " + libraryDir + ", skipping");
- }
- return;
- }
- if (!libraryDir.canRead()) {
- Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
- return;
- }
- // Iterate over the files in the directory and scan .xml files
- File platformFile = null;
- for (File f : libraryDir.listFiles()) {
- // We'll read platform.xml last
- if (f.getPath().endsWith("etc/permissions/platform.xml")) {
- platformFile = f;//先不处理platform.xml
- continue;
- }
- if (!f.getPath().endsWith(".xml")) {
- Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
- continue;
- }
- if (!f.canRead()) {
- Slog.w(TAG, "Permissions library file " + f + " cannot be read");
- continue;
- }
- readPermissionsFromXml(f, onlyFeatures);
- }
- // Read platform permissions last so it will take precedence
- if (platformFile != null) {//这边再来处理platform.xml
- readPermissionsFromXml(platformFile, onlyFeatures);
- }
- }
然后就readPermissionsFromXml读取各个标签放在各个成员变量中。
我们最后再来看下Permission标签的解析:
- void readPermission(XmlPullParser parser, String name)
- throws IOException, XmlPullParserException {
- name = name.intern();
- PermissionEntry perm = mPermissions.get(name);
- if (perm == null) {
- perm = new PermissionEntry(name);
- mPermissions.put(name, perm);
- }
- 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) {
- int gid = Process.getGidForName(gidStr);
- perm.gids = appendInt(perm.gids, gid);//放gid
- } else {
- Slog.w(TAG, "<group> without gid at "
- + parser.getPositionDescription());
- }
- }
- XmlUtils.skipCurrentTag(parser);
- }
- }
上面其中 final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>(); mPermissions是个map,权限的name对应是map的key,然后每个PermissionEntry 放入各个gid。
question:
Q:博主,这里讲的是开机时PackageManager如何扫描和注册已安装的应用程序信息吧, 所以并不是“应用程序安装过程”?
A:对于注册和安装,你是怎么定义的呢?
Q:下载了apk之后(比如把它存在了SD卡的某个folder里),都得经过一个installer“安装”后才会出现在应用程序列表里吧。所以我理解博主所讲的部分并不是“安装”,而是扫描和提取已安装应用的元数据。当然,我所谓的“安装”可能只是把apk们存放在PackageManager能扫描到的几个指定folder下?
A:通过网络来安装的确是可以划分为三步:1. 下载;2. 拷贝到指定目录;3. 解析AndroidManifest.xml文件得到应用程序的信息,并且保存在PackageManagerService里面。怎么给这3个步骤起名字不是重点,但是第3步确实是重点,例如,会涉及到Linux用户ID和Linux用户组ID的分配。另外,如果PackageManagerService没有一个应用程序的信息的话,那么这个应用程序是跑不起来的,你在Launcher里面也不会看得到它。但是如果你去看第3步里面涉及到的函数调用,就会很多函数都是以install开头的,所以从字面来看,我认为把第3步称为“安装”是合理的。
Q:为什么android每次启动都要做一次解析上述文件夹下的apk文件。不是第一次解析完会在/data/system生成packages.xml的文件吗?而且每次解析完都要重写这个
packages.xml文件。
A:应用程序的信息并不是完全保存在packages.xml这个文件里面,而且关机之后,是可以对系统的应用程序进行修改的。
Q:android把每个包解析出的信息会保存到package类中,在内存中, 不知是否可以把这些package类全部保存到一个文件中,下次开机就不用逐个解析了。包扫描确实很花时间,特别是apk装多了之后。
A:这样做也不现实,因为关机之后,可以把system.img解压出来,增、删或者修改apk,再放system.img打包放回去,再开机的时候,安装的信息又会改变了。
对于智能机来说,一般都不会频繁的关机和开机,有些关机只是进入休眠状态而已,所以这个不是问题。
Q:机子关了都没电了,还怎么修改system.img???难道是进入fastboot模式去修改?
A:你搜一下bootimg,看看是怎么玩的
Q:楼主,如果apk在安装的时候,通过解析,把这些信息保存到内存中,那么在重新开机后,岂不是所有的已经安装的apk都要重新解析一遍,然后同样保存到内存中,感觉非常不效率啊?
A:是的,如果彻底关机之后再开机,那么系统就会重新安装一遍所有的应用程序的,因为关机之后,我们是可以改变系统中的应用程序的,例如,增加、删除或者修改系统中的应用程序。如果不重新检查一遍的话,那么就会有问题了。在实际使用中,我们很少会彻底地关机,一般意义上的关机只是让系统深度睡眠,这种情况不会导致系统重新安装一遍系统中的应用程序。
系统除了会把应用程序的安装信息保存在内存中之外,还会保存在一个本地文件中,因为有些应用程序的安装信息无论安装多少次,都是必须保持一致的,例如,应用程序的Linux用户ID。如果不保存下来的话,那么每次应用程序重新安装之后,表现可能都会不一致。