首页 > 代码库 > Android的包管理机制浅析(二)
Android的包管理机制浅析(二)
上篇刚好说到获取到了签名信息,下面进入安装过程,直接上源码:
private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, String installerPackageName, PackageInstalledInfo res) { // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists(); ................... mLastScanError = PackageManager.INSTALL_SUCCEEDED; PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, System.currentTimeMillis(), user); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; } } else { updateSettingsLI(newPackage, installerPackageName, ull, null, res); // delete the partially installed application. the data directory will have to be // restored if it was already existing if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { // remove package from internal structures. Note that we want deletePackageX to // delete the package data and cache directories that it created in // scanPackageLocked, unless those directories existed before we even tried to // install. deletePackageLI(pkgName, UserHandle.ALL, false, null, null, dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0, res.removedInfo, true); } } }主要逻辑是扫描当前的Package如果扫描成功然后更新Settings内容,更新失败要删除package信息。
scanPackageLI方法比较长,所以只列出了其中我认为比较重要的环节
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime, UserHandle user) { //前面是一个APK完整性、是否重复的判断以及针对SystemApp的特殊设置 ........................ // Initialize package source and resource directories File destCodeFile = new File(pkg.applicationInfo.sourceDir); File destResourceFile = new File(pkg.applicationInfo.publicSourceDir); SharedUserSetting suid = null; PackageSetting pkgSetting = null; // writer // 这里是一段针对SystemApp的修改安装操作 .................................... //如果有设置shareUID的必须要判断属于同一个id的是否采用了同样的签名,首先获取到这个签名信息。 if (pkg.mSharedUserId != null) { suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, true); if (suid == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " for shared user failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } } // Just create the setting, don't add it yet. For already existing packages // the PkgSetting exists already and doesn't have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.cpuAbi, pkg.applicationInfo.flags, user, false); if (pkgSetting == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } pkg.applicationInfo.uid = pkgSetting.appId; pkg.mExtras = pkgSetting; //这里是对当前应用的签名信息进行验证,包括系统特殊组的特殊签名 if (!verifySignaturesLP(pkgSetting, pkg)) { if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { return null; } // The signature has changed, but this package is in the system // image... let's recover! pkgSetting.signatures.mSignatures = pkg.mSignatures; // However... if this package is part of a shared user, but it // doesn't match the signature of the shared user, let's fail. // What this means is that you can't change the signatures // associated with an overall shared user, which doesn't seem all // that unreasonable. //这里完成相同shareUid的签名判断 if (pkgSetting.sharedUser != null) { if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser); mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; return null; } } // File a report about this. String msg = "System package " + pkg.packageName + " signature changed; retaining data."; reportSettingsProblem(Log.WARN, msg); } // Verify that this new package doesn't have any content providers // that conflict with existing packages. Only do this if the // package isn't already installed, since we don't want to break // things that are installed. if ((scanMode&SCAN_NEW_INSTALL) != 0) { final int N = pkg.providers.size(); int i; for (i=0; i<N; i++) { PackageParser.Provider p = pkg.providers.get(i); if (p.info.authority != null) { String names[] = p.info.authority.split(";"); for (int j = 0; j < names.length; j++) { if (mProvidersByAuthority.containsKey(names[j])) { PackageParser.Provider other = mProvidersByAuthority.get(names[j]); Slog.w(TAG, "Can't install because provider name " + names[j] + " (in package " + pkg.applicationInfo.packageName + ") is already used by " + ((other != null && other.getComponentName() != null) ? other.getComponentName().getPackageName() : "?")); mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER; return null; } } } } } //这里是处理同组权限授权,即处于同一组可以设置从其他同组APK中获得指定的权限,这个是需要Manifest文件中配置adopt-permissions标签 if (pkg.mAdoptPermissions != null) { // This package wants to adopt ownership of permissions from // another package. for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) { final String origName = pkg.mAdoptPermissions.get(i); final PackageSetting orig = mSettings.peekPackageLPr(origName); if (orig != null) { if (verifyPackageUpdateLPr(orig, pkg)) { Slog.i(TAG, "Adopting permissions from " + origName + " to " + pkg.packageName); mSettings.transferPermissionsLPw(origName, pkg.packageName); } } } } } final String pkgName = pkg.packageName; final long scanFileTime = scanFile.lastModified(); final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0; //进程名,如果配置了进程名就以配置为准,没有就是默认包名 pkg.applicationInfo.processName = fixProcessName( pkg.applicationInfo.packageName, pkg.applicationInfo.processName, pkg.applicationInfo.uid); //下面都是针对安装APK的data目录进行处理 File dataPath; if (mPlatformPackage == pkg) { // The system package is special. dataPath = new File (Environment.getDataDirectory(), "system"); pkg.applicationInfo.dataDir = dataPath.getPath(); } else { // This is a normal package, need to make its data directory. dataPath = getDataPathForPackage(pkg.packageName, 0); boolean uidError = false; //处理应用数据存储路径存在的情况 if (dataPath.exists()) { int currentUid = 0; try { StructStat stat = Os.stat(dataPath.getPath()); currentUid = stat.st_uid; } catch (ErrnoException e) { Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e); } // If we have mismatched owners for the data path, we have a problem. if (currentUid != pkg.applicationInfo.uid) { boolean recovered = false; //这里是处理同样的APK修改了UID的情况 if (currentUid == 0) { // The directory somehow became owned by root. Wow. // This is probably because the system was stopped while // installd was in the middle of messing with its libs // directory. Ask installd to fix that. int ret = mInstaller.fixUid(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.uid); if (ret >= 0) { recovered = true; String msg = "Package " + pkg.packageName + " unexpectedly changed to uid 0; recovered to " + + pkg.applicationInfo.uid; reportSettingsProblem(Log.WARN, msg); } } //系统APK需要删除Data目录后重新创建 if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0 || (scanMode&SCAN_BOOTING) != 0)) { // If this is a system app, we can at least delete its // current data so the application will still work. int ret = removeDataDirsLI(pkgName); if (ret >= 0) { // TODO: Kill the processes first // Old data gone! String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0 ? "System package " : "Third party package "; String msg = prefix + pkg.packageName + " has changed from uid: " + currentUid + " to " + pkg.applicationInfo.uid + "; old data erased"; reportSettingsProblem(Log.WARN, msg); recovered = true; // And now re-install the app. ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.seinfo); if (ret == -1) { // Ack should not happen! msg = prefix + pkg.packageName + " could not have data directory re-created after delete."; reportSettingsProblem(Log.WARN, msg); mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } } if (!recovered) { mHasSystemUidErrors = true; } } else if (!recovered) { // If we allow this install to proceed, we will be broken. // Abort, abort! mLastScanError = PackageManager.INSTALL_FAILED_UID_CHANGED; return null; } if (!recovered) { pkg.applicationInfo.dataDir = "/mismatched_uid/settings_" + pkg.applicationInfo.uid + "/fs_" + currentUid; pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir; String msg = "Package " + pkg.packageName + " has mismatched uid: " + currentUid + " on disk, " + pkg.applicationInfo.uid + " in settings"; // writer synchronized (mPackages) { mSettings.mReadMessages.append(msg); mSettings.mReadMessages.append('\n'); uidError = true; if (!pkgSetting.uidError) { reportSettingsProblem(Log.ERROR, msg); } } } } pkg.applicationInfo.dataDir = dataPath.getPath(); //如果需要恢复seinfo执行恢复操作 if (mShouldRestoreconData) { Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued."); mInstaller.restoreconData(pkg.packageName, pkg.applicationInfo.seinfo, pkg.applicationInfo.uid); } } else { //如果应用数据存储路径不存在则直接创建 if (DEBUG_PACKAGE_SCANNING) { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) Log.v(TAG, "Want this data dir: " + dataPath); } //invoke installer to do the actual installation int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid, pkg.applicationInfo.seinfo); if (ret < 0) { // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return null; } if (dataPath.exists()) { pkg.applicationInfo.dataDir = dataPath.getPath(); } else { Slog.w(TAG, "Unable to create data directory: " + dataPath); pkg.applicationInfo.dataDir = null; } } /* * Set the data dir to the default "/data/data/<package name>/lib" * if we got here without anyone telling us different (e.g., apps * stored on SD card have their native libraries stored in the ASEC * container with the APK). * * This happens during an upgrade from a package settings file that * doesn't have a native library path attribute at all. */ if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) { if (pkgSetting.nativeLibraryPathString == null) { setInternalAppNativeLibraryPath(pkg, pkgSetting); } else { pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString; } } pkgSetting.uidError = uidError; } String path = scanFile.getPath(); /* Note: We don't want to unpack the native binaries for * system applications, unless they have been updated * (the binaries are already under /system/lib). * Also, don't unpack libs for apps on the external card * since they should have their libraries in the ASEC * container already. * * In other words, we're going to unpack the binaries * only for non-system apps and system app upgrades. */ if (pkg.applicationInfo.nativeLibraryDir != null) { //这里面的操作针对APK的本地Lib文件的处理,包括拷贝文件以及对CPU ABI支持进行判断 } pkg.mScanPath = path; if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) { // We don't do this here during boot because we can do it all // at once after scanning all existing packages. // // We also do this *before* we perform dexopt on this package, so that // we can avoid redundant dexopts, and also to make sure we've got the // code and package path correct. if (!adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg, forceDex, (scanMode & SCAN_DEFER_DEX) != 0)) { mLastScanError = PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE; return null; } } //进行odex的优化 if ((scanMode&SCAN_NO_DEX) == 0) { if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } } if (mFactoryTest && pkg.requestedPermissions.contains( android.Manifest.permission.FACTORY_TEST)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST; } ArrayList<PackageParser.Package> clientLibPkgs = null; // writer synchronized (mPackages) { if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { // Only system apps can add new shared libraries. if (pkg.libraryNames != null) { for (int i=0; i<pkg.libraryNames.size(); i++) { String name = pkg.libraryNames.get(i); boolean allowed = false; if (isUpdatedSystemApp(pkg)) { // New library entries can only be added through the // system image. This is important to get rid of a lot // of nasty edge cases: for example if we allowed a non- // system update of the app to add a library, then uninstalling // the update would make the library go away, and assumptions // we made such as through app install filtering would now // have allowed apps on the device which aren't compatible // with it. Better to just have the restriction here, be // conservative, and create many fewer cases that can negatively // impact the user experience. final PackageSetting sysPs = mSettings .getDisabledSystemPkgLPr(pkg.packageName); if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) { for (int j=0; j<sysPs.pkg.libraryNames.size(); j++) { if (name.equals(sysPs.pkg.libraryNames.get(j))) { allowed = true; allowed = true; break; } } } } else { allowed = true; } if (allowed) { if (!mSharedLibraries.containsKey(name)) { mSharedLibraries.put(name, new SharedLibraryEntry(null, pkg.packageName)); } else if (!name.equals(pkg.packageName)) { Slog.w(TAG, "Package " + pkg.packageName + " library " + name + " already exists; skipping"); } } else { Slog.w(TAG, "Package " + pkg.packageName + " declares lib " + name + " that is not declared on system image; skipping"); } } if ((scanMode&SCAN_BOOTING) == 0) { // If we are not booting, we need to update any applications // that are clients of our shared library. If we are booting, // this will all be done once the scan is complete. clientLibPkgs = updateAllSharedLibrariesLPw(pkg); } } } } // We also need to dexopt any apps that are dependent on this library. Note that // if these fail, we should abort the install since installing the library will // result in some apps being broken. if (clientLibPkgs != null) { if ((scanMode&SCAN_NO_DEX) == 0) { for (int i=0; i<clientLibPkgs.size(); i++) { PackageParser.Package clientPkg = clientLibPkgs.get(i); if (performDexOptLI(clientPkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } } } } // Request the ActivityManager to kill the process(only for existing packages) // so that we do not end up in a confused state while the user is still using the older // version of the application while the new one gets installed. if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { // If the package lives in an asec, tell everyone that the container is going // away so they can clean up any references to its resources (which would prevent // vold from being able to unmount the asec) if (isForwardLocked(pkg) || isExternal(pkg)) { if (DEBUG_INSTALL) { Slog.i(TAG, "upgrading pkg " + pkg + " is ASEC-hosted -> UNAVAILABLE"); } final int[] uidArray = new int[] { pkg.applicationInfo.uid }; final ArrayList<String> pkgList = new ArrayList<String>(1); pkgList.add(pkg.applicationInfo.packageName); sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null); } // Post the request that it be killed now that the going-away broadcast is en route killApplication(pkg.applicationInfo.packageName, pkg.applicationInfo.uid, "update pkg"); } // Also need to kill any apps that are dependent on the library. if (clientLibPkgs != null) { for (int i=0; i<clientLibPkgs.size(); i++) { PackageParser.Package clientPkg = clientLibPkgs.get(i); killApplication(clientPkg.applicationInfo.packageName, clientPkg.applicationInfo.uid, "update lib"); } } // writer synchronized (mPackages) { // We don't expect installation to fail beyond this point, if ((scanMode&SCAN_MONITOR) != 0) { mAppDirs.put(pkg.mPath, pkg); } // Add the new setting to mSettings // Add the new setting to mPackages // Make sure we don't accidentally delete its data. // Take care of first install / last update times. // Add the package's KeySets to the global KeySetManager // 后面的就是将当前安装APK的provider,receiver,activity,权限等等一些信息保存到PackageManagerService的类成员变量中,以后其他逻辑可以直接获取 // 比如启动Activity时直接从mActivities中查询,这是一个ActivityIntentResolver它里面有一个ComponentName跟PackageParser.Activity的键值对。其他的也类似 // 这就相当于将当前APK的信息注册到了系统中 } return pkg; }
总结一下这个过程,就是先检查APK的合法性,包括文件合法性和签名验证;然后是对data的数据目录做对应的处理,对lib目录进行处理;下来是进行odex的优化;再接下来是重启进程和通知资源改变;最后就是将安装的APK的所有基本信息保存到服务的各类全局变量中,以便在系统运行时进行获取和处理。解析成功之后,还需要更新一下Settings这个东东,这里面干了不好有效的事情呢。注意一下这里有两个安装的状态:PKG_INSTALL_INCOMPLETE和PKG_INSTALL_COMPLETE两种,没有把dex文件移动成功还是会失败的哟。下面把grantPermissionsLPw单独拿出来看看。
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res) { String pkgName = newPackage.packageName; synchronized (mPackages) { //write settings. the installStatus will be incomplete at this stage. //note that the new package setting would have already been //added to mPackages. It hasn't been persisted yet. mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE); mSettings.writeLPr(); } if ((res.returnCode = moveDexFilesLI(newPackage)) != PackageManager.INSTALL_SUCCEEDED) { // Discontinue if moving dex files failed. return; } if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.mPath); synchronized (mPackages) { updatePermissionsLPw(newPackage.packageName, newPackage, UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0)); // For system-bundled packages, we assume that installing an upgraded version // of the package implies that the user actually wants to run that new code, // so we enable the package. if (isSystemApp(newPackage)) { // NB: implicit assumption that system package upgrades apply to all users if (DEBUG_INSTALL) { Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName); } PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { if (res.origUsers != null) { for (int userHandle : res.origUsers) { ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userHandle, installerPackageName); } } // Also convey the prior install/uninstall state if (allUsers != null && perUserInstalled != null) { for (int i = 0; i < allUsers.length; i++) { if (DEBUG_INSTALL) { Slog.d(TAG, " user " + allUsers[i] + " => " + perUserInstalled[i]); } ps.setInstalled(perUserInstalled[i], allUsers[i]); } // these install state changes will be persisted in the // upcoming call to mSettings.writeLPr(). } } } res.name = pkgName; res.uid = newPackage.applicationInfo.uid; res.pkg = newPackage; mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE); mSettings.setInstallerPackageName(pkgName, installerPackageName); res.returnCode = PackageManager.INSTALL_SUCCEEDED; //to update install status mSettings.writeLPr(); } }
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) { final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { return; } final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; HashSet<String> origPermissions = gp.grantedPermissions; boolean changedPermission = false; //如果是替换的话,先要保存原始的权限,然后清空。 if (replace) { ps.permissionsFixed = false; if (gp == ps) { origPermissions = new HashSet<String>(gp.grantedPermissions); gp.grantedPermissions.clear(); gp.gids = mGlobalGids; } } if (gp.gids == null) { gp.gids = mGlobalGids; } //这里是列出了所有当前应用请求授予的权限 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) { Slog.w(TAG, "Unknown permission " + name + " in package " + pkg.packageName); continue; } final String perm = bp.name; boolean allowed; boolean allowedSig = false; 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). // 这个只针对非PROTECTION_SIGNATURE级别的权限 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 { 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 { 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; }更新Settings中各个APK的信息和状态,这些会同步写到 "data/system/packages.xml"、"packages-backup.xml")、 "packages.list")、"packages-stopped.xml")和 "packages-stopped-backup.xml"几个系统配置文件中。其中比较重要的grantPermissionsLPw()拿出来看看,这里是进行APK权限的授予,包括普通权限的直接授予,特殊权限的组(签名)认证以及同组权限的共享。
Android在linux UID之上的权限管理及安全的核心就在这里,主要是签名、用户组和权限级别控制,那么它们之间有哪些关系了,这一部分我们单独拿出来说一下:从ShareUserSettings和GrantedPermissions类的构成就可以看出一二了。用户组在Android上的本质上就是一系列被授予权限集合的概念(当然了,它包含的UID是linux的用户管理机制),可以用很多的Package采用相同的用户组,并且它们必须具有相同的数字签名。
final class SharedUserSetting extends GrantedPermissions { final String name; int userId; // flags that are associated with this uid, regardless of any package flags int uidFlags; final HashSet<PackageSetting> packages = new HashSet<PackageSetting>(); final PackageSignatures signatures = new PackageSignatures(); SharedUserSetting(String _name, int _pkgFlags) { super(_pkgFlags); uidFlags = _pkgFlags; name = _name; } @Override public String toString() { return "SharedUserSetting{" + Integer.toHexString(System.identityHashCode(this)) + " " + name + "/" + userId + "}"; } void removePackage(PackageSetting packageSetting) { if (packages.remove(packageSetting)) { // recalculate the pkgFlags for this shared user if needed if ((this.pkgFlags & packageSetting.pkgFlags) != 0) { int aggregatedFlags = uidFlags; for (PackageSetting ps : packages) { aggregatedFlags |= ps.pkgFlags; } setFlags(aggregatedFlags); } } } void addPackage(PackageSetting packageSetting) { if (packages.add(packageSetting)) { setFlags(this.pkgFlags | packageSetting.pkgFlags); } } }
class GrantedPermissions { int pkgFlags; HashSet<String> grantedPermissions = new HashSet<String>(); int[] gids; GrantedPermissions(int pkgFlags) { setFlags(pkgFlags); } @SuppressWarnings("unchecked") GrantedPermissions(GrantedPermissions base) { pkgFlags = base.pkgFlags; grantedPermissions = (HashSet<String>) base.grantedPermissions.clone(); if (base.gids != null) { gids = base.gids.clone(); } } void setFlags(int pkgFlags) { this.pkgFlags = pkgFlags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PRIVILEGED | ApplicationInfo.FLAG_FORWARD_LOCK | ApplicationInfo.FLAG_EXTERNAL_STORAGE); } }
另外网上还有两个经典的观点这里也摘过来,这都是上面分析可以得出的:
1、 两个程序有相同的UID,并不意味着它们会运行在同一个进程中。一个程序运行在哪一个进程,一般是由它的Package名称和UID来决定的,也就是说,UID相同但是Package名称不同的两个程序是运行两个不同的进程中的(相同的UID和签名并包名相同会覆盖安装)。这一点你可以看一下这篇文章:http://blog.csdn.net/luoshengyang/article/details/6689748
2、 给每一个程序都分配一个UID是用来控制权限的,因此,两个程序具有相同的UID,就意味它们有相同的权限,可以进行资源共享。相同UID的资源共享只是针对linux文件系统的访问权控制,不同进程间的数据是无法共享访问的。
前面还有个细节问题在看的过程中忽略了,应用权限声明和授予权限是如何生效的呢?其实对服务来说,权限只是一个字符串而已,一种是Framework定义好了的(其实也是系统应用或者服务要检查用到了才定义),比如读写外部存储的权限等等,还有一种是应用申明的(申明时还可以带上权限的级别)。所有这些权限都会保存在settings中,然后呢,权限都有一个级别的概念,对于普通和以下级别权限只要应用在manifest文件中写上并且在settings中有就可以授予,危险级别的就需要系统APK和签名咯才会授予。至于具体起作用其实是在应用请求时会去比对当前请求是否需要权限并且你这个应用是否被授予了这种权限。很多地方都有类似于checkPermission的方法,最终都会调用到ActivityManager中的checkComponentPermission()这个方法。
/** @hide */ public static int checkComponentPermission(String permission, int uid, int owningUid, boolean exported) { // Root, system server get to do everything. if (uid == 0 || uid == Process.SYSTEM_UID) { return PackageManager.PERMISSION_GRANTED; } // Isolated processes don't get any permissions. if (UserHandle.isIsolated(uid)) { return PackageManager.PERMISSION_DENIED; } // If there is a uid that owns whatever is being accessed, it has // blanket access to it regardless of the permissions it requires. if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) { return PackageManager.PERMISSION_GRANTED; } // If the target is not exported, then nobody else can get to it. if (!exported) { return PackageManager.PERMISSION_DENIED; } if (permission == null) { return PackageManager.PERMISSION_GRANTED; } try { return AppGlobals.getPackageManager() .checkUidPermission(permission, uid); } catch (RemoteException e) { // Should never happen, but if it does... deny! Slog.e(TAG, "PackageManager is dead?!?", e); } return PackageManager.PERMISSION_DENIED; }最终还是调用到了PackageManagerService中的checkUidPermission方法:
@Override public int checkUidPermission(String permName, int uid) { synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj != null) { GrantedPermissions gp = (GrantedPermissions)obj; if (gp.grantedPermissions.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } } else { HashSet<String> perms = mSystemPermissions.get(uid); if (perms != null && perms.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } } } return PackageManager.PERMISSION_DENIED; }看到了吧,最终还是看gp.grantedPermissions中有没有这个字符串的问题,这跟前面授予权限就对应上了吧。
这两篇说的东西都很杂,看完对包管理服务框架还是没有什么整体的认识,接下来我就要在下篇开始站高度啦,开始讲讲整体架构。
Android的包管理机制浅析(二)