Android PMS原理总结
一 system_server啟動PMS
Android的所有Java服務都是通過system_server進程啟動的,并且駐留在system_server進程中。SystemServer進程在啟動時,通過創建一個ServerThread線程啟動所有服務。
1.1 startBootstrapServices()
system_server的startBootstrapServices()函數會啟動一些引導服務,比如:
- ActivityManagerService
- PowerManagerService
- DisplayManagerService
- SensorService
其中PackageManagerService就在這里啟動。
private void startBootstrapServices() {//啟動installer服務Installer installer = mSystemServiceManager.startService(Installer.class);// We need the default display before we can initialize the package manager.mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);//處于加密狀態則僅僅解析核心應用// Only run "core" apps if we're encrypting the device.String cryptState = SystemProperties.get("vold.decrypt");if (ENCRYPTING_STATE.equals(cryptState)) {Slog.w(TAG, "Detected encryption in progress - only parsing core apps");mOnlyCore = true;} else if (ENCRYPTED_STATE.equals(cryptState)) {Slog.w(TAG, "Device encrypted - only parsing core apps");mOnlyCore = true;}// 創建PMS對象 - 啟動入口traceBeginAndSlog("StartPackageManagerService");mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);// 是否首次啟動mFirstBoot = mPackageManagerService.isFirstBoot();// 獲取PackageManagermPackageManager = mSystemContext.getPackageManager();Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); }1.2 startOtherService()
system_server的startOtherService()方法會啟動其他服務,在這里也會對PMS做一些處理
private void startOtherServices() {......if (!mOnlyCore) {........try {//將調用performDexOpt:Performs dexopt on the set of packagesmPackageManagerService.updatePackagesIfNeeded();}...............try {//執行Fstrim,執行磁盤維護操作//可能類似于TRIM技術,將標記為刪除的文件,徹底從硬盤上移除//而不是等到寫入時再移除,目的是提高寫入時效率mPackageManagerService.performFstrimIfNeeded();}................try {mPackageManagerService.systemReady();}...............} }PMS啟動后將參與一些系統優化的工作,然后調用SystemReady方法通知系統服務進入就緒狀態
在system_server進程啟動過程,涉及PMS服務的主要幾個處理:
- PMS.main()
- PMS.performDexOpt()
- PMS.systemReady()
二 PMS.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入口中,直接實例化一個PMS服務,并將PMS服務放入ServiceManager中,便于管理,那么重點就是實例化PMS
代碼比較多,選擇一些重點
new PackageManagerService(context, installer, factoryTest, onlyCore);
public PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,SystemClock.uptimeMillis());if (mSdkVersion <= 0) {Slog.w(TAG, "**** ro.build.version.sdk not set!");}mContext = context;mFactoryTest = factoryTest;mOnlyCore = onlyCore;mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));// displayMetrics是一個描述界面顯示,尺寸,分辨率,密度的類mMetrics = new DisplayMetrics();// Settings是Android的全局管理者,用于協助PMS保存所有的安裝包信息mSettings = new Settings(context);mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,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);.......mInstaller = installer;// 獲取默認的顯示信息,保存到mMetricsgetDefaultDisplayMetrics(context, mMetrics);// 獲取系統配置信息SystemConfig systemConfig = SystemConfig.getInstance();mGlobalGids = systemConfig.getGlobalGids();mSystemPermissions = systemConfig.getSystemPermissions();mAvailableFeatures = systemConfig.getAvailableFeatures();synchronized (mInstallLock) {// writersynchronized (mPackages) {mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();mHandler = new PackageHandler(mHandlerThread.getLooper());Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);// 創建各種目錄,此時,到了我們熟悉的Android目錄了File dataDir = Environment.getDataDirectory();mAppDataDir = new File(dataDir, "data");mAppInstallDir = new File(dataDir, "app");mAppLib32InstallDir = new File(dataDir, "app-lib");mAsecInternalPath = new File(dataDir, "app-asec").getPath();mUserAppDataDir = new File(dataDir, "user");mDrmAppPrivateInstallDir = new File(dataDir, "app-private");// 創建用戶管理服務sUserManager = new UserManagerService(context, this,mInstallLock, mPackages);.......}....... }此方法中,做了大概如下操作
- 構造DisplayMetrics類:描述界面顯示,尺寸,分辨率,密度。構造完后并獲取默認的信息保存到變量mMetrics中
- 構造Settings類:這個是Android的全局管理者,用于協助PMS保存所有的安裝包信息
- 保存Installer對象
- 獲取系統配置信息:SystemConfig構造函數中會通過readPermissions()解析指定目錄下的所有xml文件,然后把這些信息保存到systemConfig中,涉及的目錄有如下:
-
- /system/etc/sysconfig
- /system/etc/permissions
-
- /oem/etc/sysconfig
- /oem/etc/permissions
- 創建data下的各種目錄,比如data/app, data/app-private等
其中我們最為熟悉的就是最后一點,創建各種Android目錄結構
各種第三方應用都是安裝在mAppInstallDir目錄中,直接搜索mAppInstallDir,得知在scanDirLI()函數中調用
掃描指定文件目錄下的apk文件
scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime)
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {final File[] files = dir.listFiles();if (ArrayUtils.isEmpty(files)) {Log.d(TAG, "No files in app dir " + dir);return;}if (DEBUG_PACKAGE_SCANNING) {Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags+ " flags=0x" + Integer.toHexString(parseFlags));}for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue;}try {scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,scanFlags, currentTime, null);} catch (PackageManagerException e) {Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());// Delete invalid userdata appsif ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);if (file.isDirectory()) {FileUtils.deleteContents(file);}file.delete();}}}}掃描這個文件夾下面的所有文件,并且判斷是否為apk文件,如果不是繼續循環,如果是,則掃描這個路徑下的文件,調用scanPackageLI()
解析package信息,其中最重要的一個類為PackageParser,這個類和插件化技術處理聯系緊密
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) throws PackageManagerException {if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);parseFlags |= mDefParseFlags;PackageParser pp = new PackageParser();pp.setSeparateProcesses(mSeparateProcesses);pp.setOnlyCoreApps(mOnlyCore);pp.setDisplayMetrics(mMetrics);....... final PackageParser.Package pkg;try {// 1pkg = pp.parsePackage(scanFile, parseFlags);} catch (PackageParserException e) {throw PackageManagerException.from(e);} }其中1位置使用PackageParser對象解析對應scanFile文件下的資源,最終返回一個pkg對象,pkg為PackageParser類中的內部類Package
Package:
public final static class Package {public String packageName;/** Names of any split APKs, ordered by parsed splitName */public String[] splitNames;// TODO: work towards making these paths invariant/*** Path where this package was found on disk. For monolithic packages* this is path to single base APK file; for cluster packages this is* path to the cluster directory.*/public String codePath;/** Path of base APK */public String baseCodePath;/** Paths of any split APKs, ordered by parsed splitName */public String[] splitCodePaths;/** Flags of any split APKs; ordered by parsed splitName */public int[] splitFlags;public boolean baseHardwareAccelerated;// For now we only support one application per package.public final ApplicationInfo applicationInfo = new ApplicationInfo();public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);public final ArrayList<Activity> activities = new ArrayList<Activity>(0);public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);public final ArrayList<Provider> providers = new ArrayList<Provider>(0);public final ArrayList<Service> services = new ArrayList<Service>(0);public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);....... ....... ....... }其中有我們熟悉的activities,receivers,providers,services四大組件集合,Package對象就是包含了我們Android的所有資源信息
PackageParser中的parsePackage()方法--->parseMonolithicPackage()
public Package parsePackage(File packageFile, int flags) throws PackageParserException {if (packageFile.isDirectory()) {return parseClusterPackage(packageFile, flags);} else {return parseMonolithicPackage(packageFile, flags);}}@Deprecatedpublic Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {if (mOnlyCoreApps) {final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);if (!lite.coreApp) {throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,"Not a coreApp: " + apkFile);}}// 1final AssetManager assets = new AssetManager();try {final Package pkg = parseBaseApk(apkFile, assets, flags);pkg.codePath = apkFile.getAbsolutePath();return pkg;} finally {IoUtils.closeQuietly(assets);}}在代碼中位置1 ,初始化一個AssetManager對象,并作為參數傳入parseBaseApk()函數,最終返回一個package對象
由此可見,parseBaseApk函數也是對這個apk文件的資源進行解析處理
注意:此時new出來的assetManager并不能直接加載apk資源,必須還要去調用addAssetPath方法
parseBaseApk():
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)throws PackageParserException {final String apkPath = apkFile.getAbsolutePath();mParseError = PackageManager.INSTALL_SUCCEEDED;mArchiveSourcePath = apkFile.getAbsolutePath();if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);// 1 final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);Resources res = null;XmlResourceParser parser = null;try {res = new Resources(assets, mMetrics, null);assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,Build.VERSION.RESOURCES_SDK_INT);// 2 parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);final String[] outError = new String[1];// 3final Package pkg = parseBaseApk(res, parser, flags, outError);if (pkg == null) {throw new PackageParserException(mParseError,apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);}pkg.baseCodePath = apkPath;pkg.mSignatures = null;return pkg;} catch (PackageParserException e) {throw e;} catch (Exception e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to read manifest from " + apkPath, e);} finally {IoUtils.closeQuietly(parser);}}位置1代碼為
private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)throws PackageParserException {if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,"Invalid package file: " + apkPath);}// The AssetManager guarantees uniqueness for asset paths, so if this asset path// already exists in the AssetManager, addAssetPath will only return the cookie// assigned to it.int cookie = assets.addAssetPath(apkPath);if (cookie == 0) {throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,"Failed adding asset path: " + apkPath);}return cookie; }此時,可以通過assetManager加載apk資源,包括res,androidMainfest.xml文件等
位置2 打開androidMainfest文件,也就是解析關鍵資源入口就找到了
位置3 將解析后的androidMainfest文件返回的parser對象傳入parseBaseApk函數
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError) throws XmlPullParserException, IOException {....... ....... 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("application")) {if (foundApp) {if (RIGID_PARSER) {outError[0] = "<manifest> has more than one <application>";mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return null;} else {Slog.w(TAG, "<manifest> has more than one <application>");XmlUtils.skipCurrentTag(parser);continue;}}foundApp = true;//1if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {return null;}}....... }....... }在這個方法中,解析了AndroidMainfest中的application節點,這是我們需要關心的重點。
直接看位置1
private boolean parseBaseApplication(Package owner, Resources res,XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)throws XmlPullParserException, IOException {....... .......while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {continue;}String tagName = parser.getName();if (tagName.equals("activity")) {// 1Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,owner.baseHardwareAccelerated);if (a == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.activities.add(a);} else if (tagName.equals("receiver")) {// 2Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);if (a == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.receivers.add(a);} else if (tagName.equals("service")) {Service s = parseService(owner, res, parser, attrs, flags, outError);if (s == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.services.add(s);} else if (tagName.equals("provider")) {Provider p = parseProvider(owner, res, parser, attrs, flags, outError);if (p == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.providers.add(p);} else if (tagName.equals("activity-alias")) {Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);if (a == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}owner.activities.add(a);} else if (parser.getName().equals("meta-data")) {// note: application meta-data is stored off to the side, so it can// remain null in the primary copy (we like to avoid extra copies because// it can be large)if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,outError)) == null) {mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}} else if (tagName.equals("library")) {sa = res.obtainAttributes(attrs,com.android.internal.R.styleable.AndroidManifestLibrary);// Note: don't allow this value to be a reference to a resource// that may change.String lname = sa.getNonResourceString(com.android.internal.R.styleable.AndroidManifestLibrary_name);sa.recycle();if (lname != null) {lname = lname.intern();if (!ArrayUtils.contains(owner.libraryNames, lname)) {owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);}}XmlUtils.skipCurrentTag(parser);} else if (tagName.equals("uses-library")) {sa = res.obtainAttributes(attrs,com.android.internal.R.styleable.AndroidManifestUsesLibrary);// Note: don't allow this value to be a reference to a resource// that may change.String lname = sa.getNonResourceString(com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);boolean req = sa.getBoolean(com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,true);sa.recycle();if (lname != null) {lname = lname.intern();if (req) {owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);} else {owner.usesOptionalLibraries = ArrayUtils.add(owner.usesOptionalLibraries, lname);}}XmlUtils.skipCurrentTag(parser);} else if (tagName.equals("uses-package")) {// Dependencies for app installers; we don't currently try to// enforce this.XmlUtils.skipCurrentTag(parser);} else {if (!RIGID_PARSER) {Slog.w(TAG, "Unknown element under <application>: " + tagName+ " at " + mArchiveSourcePath + " "+ parser.getPositionDescription());XmlUtils.skipCurrentTag(parser);continue;} else {outError[0] = "Bad element under <application>: " + tagName;mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return false;}}} }可以看到上面就是我們最為熟悉的配置文件中的各種節點配置,包括四大組件,meta-data
等。
其中有一點可以注意下:activity節點和receiver節點處理類型都為Activity對象,這是因為activity和receiver節點結構一樣,類似于JavaBean結構。
activity和receiver節點結構:
<activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><receiver android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /></intent-filter></receiver>節點中都有intent-filter,action節點。那么在上面parseBaseApplication()函數代碼塊中位置1和位置2的函數parseActivity()如下:
private Activity parseActivity(Package owner, Resources res,XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,boolean receiver, boolean hardwareAccelerated)throws XmlPullParserException, IOException {......// 1Activity a = new Activity(mParseActivityArgs, new ActivityInfo());......while ((type=parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG|| parser.getDepth() > outerDepth)) {if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {continue;}if (parser.getName().equals("intent-filter")) {//2ActivityIntentInfo intent = new ActivityIntentInfo(a);if (!parseIntent(res, parser, attrs, true, intent, outError)) {return null;}if (intent.countActions() == 0) {Slog.w(TAG, "No actions in intent filter at "+ mArchiveSourcePath + " "+ parser.getPositionDescription());} else {a.intents.add(intent);}} else if (parser.getName().equals("meta-data")) {if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,outError)) == null) {return null;}}}...... }13行直接匹配解析intent-filter節點,同時將parseIntent()函數返回的intent-filter放入activity對象中的intents集合中。
在第8行中,這個activity對象中會將信息轉為ActivityInfo,其中包括各種信息:name,
packageName等等
到這里基本一次解析就結束了,會將得到的信息存儲到對應的PackageParser中的對應的集合中,并將這部分信息傳遞到PMS中
那么一樣的道理,在其他文件路徑下的處理也類似,比如/data/app-private目錄
以上,為PMS原理簡單流程,后續可補充。
總結
以上是生活随笔為你收集整理的Android PMS原理总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在OpenCV里使用图片修复
- 下一篇: android sina oauth2.