編輯:關於Android編程
Android既然基於linux,那我們能不能將c/c++代碼交叉編譯成可執行文件然後放到目標機器上跑呢?當然可以,不過前提你得有執行權限,事實上,android有一部分後台服務是純linux程序(不需要davik虛擬機資源),比如service manager和media server等。
Android應用沒有權限啟動linux程序,同樣的也無法主動從zygotefork出一個子進程來執行自身代碼,那一個app安裝後,如何拿到這個app的入口信息?代碼文件(dex、so)以及相關資源釋放目錄的權限如何設置?APP運行時被准許的權限有哪些?這些都是PMS在掃描完一個app後需要確定的。
所以,掃描一個APK, 需要做的事情有:
1) 獲取APP暴露的所有組件及相關數據
2) 獲取APP聲明和准許的權限數據
3) 生成app id,然後基於其生成的user id來作為app本地目錄的訪問權限控制
4) 釋放代碼文件,包含dex和so文件
5) 將1和2數據緩存到PMS中,供系統運行時使用。
系統啟動後,systemserver會調用如下代碼初始化PMS
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; }
除了創建PackageManagerService並將其添加到service manager外,什麼也沒做,接著看PMS
構造函數:
1) 創建Settings對象,並通過addSharedUserLPw添加android預定義的shared user id
2) 初始化systemConfig,然後調用getPermissions獲取系統預先配置的permission以及對應group數據,用來初始化settings.permissions列表
3) 通過調用systemConfig.getSharedLibraries,獲取系統預先配置的共享庫文件(.jar)並初始化mSharedLibraries
4) 通過mSettings.readLPw讀取本地緩存文件(/data/system/package.xml)來初始化Settings,
那些數據需要緩存,我這邊總結了下,主要有settings內部的permission和permission tree列表和package內部的grant permission列表,最後修改時間,rename過的包等等,總之,那些在運行期生成或會改變的數據,就需要緩存。
5) 遍歷mSharedLibraries,對需要的library做代碼優化
6) 調用scanDirLI掃描指定目錄下的所有apk文件,主要是系統app目錄/system/app和用戶app目錄/data/app
7) 調用updateAllSharedLibrariesLPw更新所有package的usesLibraryFiles數據,為什麼要做更新?因為package在manifest裡頭使用use-library描述要引用的library,在系統mSharedLibraries裡可能是未定義的,所以,需要做數據同步
8) 調用updatePermissionsLPw更新系統permission trees和permission 列表
PMS在構造時,需要初始化SystemConfig:
SystemConfig systemConfig = SystemConfig.getInstance();
接著看構造函數:
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); }
代碼主要調用readPermissions遍歷/etc/sysconfig和/etc/permissions兩個目錄下是所有.xml文件,讀取系統預置的配置信息,主要有:
1) permission 權限以及對應的group id
2) share libraries 系統預置了哪些共享庫可供app加載使用
3) available features 系統支持哪些feature,比如是否支持前置攝像頭等
還有,readPermissions第二個參數,如果為false,那麼.xml文件內permission對應的gid會被添加到系統默認global gids並分配給啟動的app,也就是說,app無須聲明這個permission,默認就會在這個Supplementary GID對應分組中。
xml文件內對應的配置數據:
file="/system/framework/android.test.runner.jar" />
Linux權限管理是基於UID和GID,可以對目標文件設置用戶以及分組訪問權限(Read,Write,Execute),Linux進程只有屬於對應的用戶或分組才能有權限訪問該文件。
Android延續了Linux的權限管理方式,每個APK在經過PMS安裝成功後,都會分配一個主UID和主GID,還有Supplementary GIDS則是根據system config的global gids + app所有 grant permission對應的gid組合而成。
除了linux的權限管理方式之外,Android還添加了permission機制來對android系統服務做訪問控制,app如果要使用某項系統功能,必須要在manifest裡聲明該功能對應的permission並且該permission被准許後,app才能使用該系統功能。
APP所屬的GID默認跟UID一致,Android UID的管理方式主要有兩種:
1)Share User Id,也就是說,多個簽名相同的app共享同一個user id,這樣多個app就具有相同的權限,可以相互訪問資源
2)Normal User Id,每個app被會分配一個獨立的user id,這說明每個app都運行在獨立的user下,無法直接共享進程內部數據,從而保護app內部數據的安全
單純從user id分配來說,又可以分為系統預定義和動態生成,系統預定義的user id主要有Process.SYSTEM_UID(1000, 對應系統權限)和RADIO_UID(1001,對應通信相關應用權限)等等;動態生成的user id,在10000-19999之間依次分配,相關代碼如下:
// Returns -1 if we could not find an available UserId to assign private int newUserIdLPw(Object obj) { // Let's be stupidly inefficient for now... final int N = mUserIds.size(); for (int i = mFirstAvailableUid; i < N; i++) { if (mUserIds.get(i) == null) { mUserIds.set(i, obj); return Process.FIRST_APPLICATION_UID + i; } } // None left? if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) { return -1; } mUserIds.add(obj); return Process.FIRST_APPLICATION_UID + N; }
Process.FIRST_APPLICATION_UID值就是10000,mUserIds列表保存所有已經申請的package,package對應的user id就是其在mUserIds的索引加上10000;
這個函數先遍歷mUserIds,看是否存在已經釋放的user id,如果有,則將其重新分配給新申請的package,否則就將申請的packahe追加到在mUserIds。
接下去說說ShareUser Id,它對應的數據結構為:
/** * Settings data for a particular shared user ID we know about. */ 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 ArraySet packages = new ArraySet(); final PackageSignatures signatures = new PackageSignatures(); /**……..*/ }
成員函數相關代碼這裡就不拷貝了,大家可以直接去看源碼
從成員變量的定義可以看出,SharedUserSetting主要保存:
1) user id
2) packages 當前share user id的所有package信息
3) signatures share user id的app對應的簽名數據
PMS將所有的SharedUserSetting數據保存到Settings. mSharedUsers,然後通過調用Settings. addSharedUserLPw添加新的SharedUserSetting:
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) { 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; } s = new SharedUserSetting(name, pkgFlags); s.userId = uid; if (addUserIdLPw(uid, s, name)) { mSharedUsers.put(name, s); return s; } return null; }
上面介紹PMS初始化流程時介紹過,PMS初始化時會添加系統預定義的ShareUserSetting:
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);
有一點要說明下,這裡通過addSharedUserLPw添加ShareUserSetting,只設置了name,user id,flags;PackageSetting在scan package調用mSettings.getPackageLPw獲取package settings時會被更新設置;
signatues數據,則會在scan package結束調用mSettings.insertPackageSettingLPw(pkgSetting, pkg)時被更新。
主UID和GID決定了app的訪問權限,上面也提過,android還支持一套基於android framework的permission機制,這套機制確保app在調用某項android系統服務接口的時候,必須在manifest聲明對應permission,然後permission被准許後app方可訪問;
這裡還有個問題,假如有些permission對應的系統服務接口,需要訪問linux底層設備,那app對底層設備的訪問權限如何獲取?所以permission在被配置時,必須指定對應設備的訪問權限信息,也就是SupplementaryGid,這樣app啟動時才會在對應的補充分組,從而擁有對linux設備的訪問權。
比如app要訪問外部存儲,必須在manifest聲明:
android.permission.READ_EXTERNAL_STORAGE
其對應的group id配置信息如下:系統預置的permission配置如何數據獲取,可查看 2.2 章節
2.3.3.1權限定義PMS會將系統支持的所有permission數據保存到Settings. mPermissions MAP中,對應的數據類型如下:
// Mapping from permission names to info about them. final ArrayMap mPermissions = new ArrayMap();
Key是permission名字,value是對應的permission數據,數據類型為BasePermission,先看下這個類核心字段的定義:
final class BasePermission {
final static int TYPE_NORMAL = 0;
final static int TYPE_BUILTIN = 1;
final static int TYPE_DYNAMIC = 2;
//權限名
final String name;
//定義權限的包名,如果type為TYPE_BUILTIN,則為android
String sourcePackage;
//定義權限的包數據
PackageSettingBase packageSetting;
//權限類型
final int type;
//權限保護等級
int protectionLevel;
//權限數據
PackageParser.Permission perm;
PermissionInfo pendingInfo;
//權限對應的user id,這個應該是當等級為signature或者permission 類型為tree時有效
int uid;
//權限對應的Supplementary Gids
int[] gids;
}
先看type和protectionLevel兩個字段
type表示權限定義的類型,主要有三個:
1) TYPE_NORMAL,指的是正常app在manifest使用permission字段定義的權限
2) TYPE_BUILTIN,系統內置權限,PMS初始化時從SystemConfig讀取
3) TYPE_DYNAMIC,動態添加的權限,一個app能動態添加權限的前提是,需要在manifest裡定義permission tree,也就是權限樹的根結點域名,然後app才能使用addPermission來動態添加權限。
protectionLevel表示權限的保護等級,默認分四級:
1) PROTECTION_NORMAL
普通權限,低風險
2) PROTECTION_DANGEROUS
有風險的權限
3) PROTECTION_SIGNATURE
申請使用權限的app簽名必須跟權限所屬app簽名一致
4) PROTECTION_SIGNATURE_OR_SYSTEM
申請使用的必須是系統或者簽名一致app
接著看perm字段,對應的類為PackageParser.Permission,這個類對應的是manifest裡頭的permission和permission tree,這兩個權限的區別,可以看上頭type類型的介紹
PackageParser.Permission類定義:
public final static class Permission extends Component
public final PermissionInfo info;
public boolean tree;
public PermissionGroup group;
}
tree字段用於判斷是否是permission tree,group包含權限分組相關描述數據,info則是權限的描述數據
PermissionInfo類定義:
public class PermissionInfo extends PackageItemInfo implements Parcelable {
…..
}
主要包含permission name和package name等.
接著看Settings.mPermissions這個系統權限Map數據是怎麼生成的
在PMS構造時,會調用SystemConfig.getPermissions()獲取系統builtin權限數據
// Propagate permission configuration in to package manager.
ArrayMap
= systemConfig.getPermissions();
for (int i=0; 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);
}
}
遍歷systemConfig定義的permission時,接著判斷mSettings.mPermissions是否已經存在該權限的定義,如果不存在,新建一個BasePermission,權限source package為android,type為builtin,接著更新權限對應的supplementary gids;
接著看掃描apk時權限數據的初始化:
PackageParser.parsePermission
private Permission parsePermission(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestPermission);
if (!parsePackageItemInfo(owner, perm.info, outError,
"
com.android.internal.R.styleable.AndroidManifestPermission_name,
com.android.internal.R.styleable.AndroidManifestPermission_label,
com.android.internal.R.styleable.AndroidManifestPermission_icon,
com.android.internal.R.styleable.AndroidManifestPermission_logo,
com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
/……/
owner.permissions.add(perm);
return perm;
}
PackageParser.parsePermissionTree:
private Permission parsePermissionTree(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestPermissionTree);
if (!parsePackageItemInfo(owner, perm.info, outError,
"
com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
com.android.internal.R.styleable.AndroidManifestPermissionTree_logo,
com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
sa.recycle();
int index = perm.info.name.indexOf('.');
if (index > 0) {
index = perm.info.name.indexOf('.', index+1);
}
if (index < 0) {
outError[0] = "
+ perm.info.name;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
perm.info.descriptionRes = 0;
perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
perm.tree = true;
owner.permissions.add(perm);
return perm;
}
可以看出,permission和permission tree都保存到Package.permissions列表中,通過字段perm.tree是true還是false來區分是否是permissiontree
在app掃描結束後,permission數據已經被全部拿到,但是現在數據都還只是保存在Package內部,所以還需要將permission數據添加到PMS permission Map,代碼在
PMS.scanPackageDirtyLI,這個函數太長了,所以這裡只截取對permission列表處理相關代碼段
N = pkg.permissions.size();
r = null;
for (i=0; i
PackageParser.Permission p = pkg.permissions.get(i);
ArrayMap
p.tree ? mSettings.mPermissionTrees
: mSettings.mPermissions;
p.group = mPermissionGroups.get(p.info.group);
if (p.info.group == null || p.group != null) {
BasePermission bp = permissionMap.get(p.info.name);
// Allow system apps to redefine non-system permissions
if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {
final boolean currentOwnerIsSystem = (bp.perm != null
&& isSystemApp(bp.perm.owner));
if (isSystemApp(p.owner)) {
if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
// It's a built-in permission and no owner, take ownership now
bp.packageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
bp.sourcePackage = p.info.packageName;
} else if (!currentOwnerIsSystem) {
String msg = "New decl " + p.owner + " of permission "
+ p.info.name + " is system; overriding " + bp.sourcePackage;
reportSettingsProblem(Log.WARN, msg);
bp = null;
}
}
}
if (bp == null) {
bp = new BasePermission(p.info.name, p.info.packageName,
BasePermission.TYPE_NORMAL);
permissionMap.put(p.info.name, bp);
}
if (bp.perm == null) {
if (bp.sourcePackage == null
|| bp.sourcePackage.equals(p.info.packageName)) {
BasePermission tree = findPermissionTreeLP(p.info.name);
if (tree == null
|| tree.sourcePackage.equals(p.info.packageName)) {
bp.packageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
bp.sourcePackage = p.info.packageName;
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(p.info.name);
}
} else {
Slog.w(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " ignored: base tree "
+ tree.name + " is from package "
+ tree.sourcePackage);
}
} else {
Slog.w(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " ignored: original from "
+ bp.sourcePackage);
}
} else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append("DUP:");
r.append(p.info.name);
}
if (bp.perm == p) {
bp.protectionLevel = p.info.protectionLevel;
}
} else {
Slog.w(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " ignored: no group "
+ p.group);
}
從代碼可以看出,雖然permission和permission true在package內部是保存到一個list的,但是到了Settings,它們被分開了,分別對應mSettings.mPermissions和mSettings.mPermissionTrees,代碼邏輯也比較簡單,一開始先判斷分組是否配置准確:
p.info.group == null || p.group != null
要麼不設置權限分組,如果設置了,那就必須在mPermissionGroups能拿到對應數據,這兩者都OK,接著根據權限名稱從permissionMap拿到對應BasePermission對象,對第一次掃描apk來說,bp肯定為null,接著創建一個normal的BasePermission並添加到permissionMap,然後判斷權限數據對象perm 是否為null,如果為null,才可對其進行更新,更新之前,還需要做兩次判斷:
1) sourcePackage未定義或者包名相同
2) 判斷是否存在匹配的已定義permission tree根節點域名,如果不存在就沒問題,反之如果存在,則必須保證permissiontree所屬package跟permission是一致的,這也說明,針對app定義在manifest裡的permission,可不定義permission tree根節點域名
上面兩個條件都滿足後,就可以更新bp. packageSetting和bp. sourcePackage等數據了,最後更新bp.protectionLevel權限等級.
在所有apk數據掃描結束後, 最後調用PMS.updatePermissionsLPw刷新PMS permission MAP:
private void updatePermissionsLPw(String changingPkg,
PackageParser.Package pkgInfo, int flags) {
// Make sure there are no dangling permission trees.
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) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
changingPkg);
}
}
}
if (pkgInfo != null) {
grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
}
}
刷新的目的有兩個,一個是依次看看mSettings.mPermissionTrees和mSettings.mPermissions是否存在未設置owner package的,如果有,再根據sourcePackage name嘗試從mPackages列表中獲取對應的package,如果能拿到,更新之,否則說明這個BasePermission數據已經失效,將其刪除.
最後嘗試刷新所有app的grantpermission數據.
2.3.3.2權限使用
對大部分APP來說,如果要使用某權限,必須要在manifest進行聲明, 比如:
然後PackageParser. parseBaseApk時,會調用parseUsesPermission對聲明解析使用的權限數據:
private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
// Note: don't allow this value to be a reference to a resource
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
/*
boolean required = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
*/
boolean required = true; // Optional
int maxSdkVersion = 0;
TypedValue val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
if (val != null) {
if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
maxSdkVersion = val.data;
}
}
sa.recycle();
if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) {
if (name != null) {
int index = pkg.requestedPermissions.indexOf(name);
if (index == -1) {
pkg.requestedPermissions.add(name.intern());
pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
} else {
if (pkg.requestedPermissionsRequired.get(index) != required) {
outError[0] = "conflicting
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
}
}
XmlUtils.skipCurrentTag(parser);
return true;
}
app申請的所有權限都保存於pkg.requestedPermissions,請求使用的權限則會被保存到pkg.requestedPermissionsRequired中,required默認為true,說明所有申請的權限在默認情況下,都會被添加到請求使用列表。
權限請求了,接下去還去要對其進行准許(grant)操作,針對准許過後的權限,PMS定義了一個類GrantPermissions用於保存這些數據:
class GrantedPermissions {
int pkgFlags;
ArraySet
int[] gids;
}
grantedPermissions保存app所有准許過的權限,gids之前說過,它保存有app使用准許權限所需要的Supplementary Gids。
PMS根據app user id的不同,准許過的權限保存位置也不同:
1) normal user id, 對應PackageSetting
2) share user id,對應PackageSetting. sharedUser
在掃描apk結束後,會調用:
void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, int flags)
來更新系統權限數據,第二個參數pkgInfo用於指定要re-grant的包,第三個參數則是用於指定是否要re-grant所有app包。對於PMS初始化結束後,由於掃描了所有的包,所以需要re-grant所有的app包數據:
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
| (regrantPermissions
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
: 0));
接著看函數代碼:
private void updatePermissionsLPw(String changingPkg,
PackageParser.Package pkgInfo, int flags) {
// Make sure there are no dangling permission trees.
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) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
changingPkg);
}
}
}
if (pkgInfo != null) {
grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
}
}
這個函數主要做如下事情:
1) 同步mPermissionTrees和mPermissions數據,清除沒有owner package的權限
2) 如果flags有設置UPDATE_PERMISSIONS_ALL,則依次對所有的package調用grantPermissionsLPw
3) 如果pkgInfo不為空,則需要對這個指定的package調用grantPermissionsLPw
grantPermissionsLPw就是針對package來進行具體的權限准許操作了:
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
boolean changedPermission = false;
if (replace) {
ps.permissionsFixed = false;
if (gp == ps) {
origPermissions = new ArraySet
gp.grantedPermissions.clear();
gp.gids = mGlobalGids;
}
}
if (gp.gids == null) {
gp.gids = mGlobalGids;
}
final int N = pkg.requestedPermissions.size();
for (int i=0; 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
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;
}
先拿到package對應的PackageSetting對象實例,然後根據是否是share user id來拿到對應的GrantPermission對象,接著遍歷package 申請的所有權限,如果權限保護等級是normal或dangerous的,只要權限被請求准許或者app是系統應用,那麼准許通過;對於保護等級是signature的,則需要調用grantSignaturePermission來核對簽名是否一致,如果一致,准許通過,最後調用gp.grantedPermissions.add(perm);保存准許通過的權限,同時調用
gp.gids= appendInts(gp.gids, bp.gids);保存BasePermission對應的supplementary gids.
至此,app對應的靜態權限數據已經全部生成。
2.3.3.3動態權限
上面說過,權限分三種,BUILTIN(系統預置),NORMAL(manifest定義)和DYNAMIC(動態添加),如果一個app要動態添加和准許權限,需要具備:
1) 在manifest定義permission tree
2) 具有"android.permission.GRANT_REVOKE_PERMISSIONS"權限,也就是准許和撤銷權限的權限。
這個權限定義在framework-res.apk對應的manifest中:
android:label="@string/permlab_grantRevokePermissions"
android:description="@string/permdesc_grantRevokePermissions"
android:protectionLevel="signature" />
其保護等級是signature的,也就是說,用這個權限的app,必須要有系統權限,對於普通app來說,你可以定義permission tree並且動態添加permission,但是你沒有權限去准許和撤銷這個權限,添加和准許權限對應的函數為PMS.addPermission和PMS.grantPermission。
先看addPermission,其最終將會調用addPermissionLocked:
boolean addPermissionLocked(PermissionInfo info, boolean async) {
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
}
BasePermission tree = checkPermissionTreeLP(info.name);
BasePermission bp = mSettings.mPermissions.get(info.name);
boolean added = bp == null;
boolean changed = true;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
if (added) {
enforcePermissionCapLocked(info, tree);
bp = new BasePermission(info.name, tree.sourcePackage,
BasePermission.TYPE_DYNAMIC);
} else if (bp.type != BasePermission.TYPE_DYNAMIC) {
throw new SecurityException(
"Not allowed to modify non-dynamic permission "
+ info.name);
} else {
if (bp.protectionLevel == fixedLevel
&& bp.perm.owner.equals(tree.perm.owner)
&& bp.uid == tree.uid
&& comparePermissionInfos(bp.perm.info, info)) {
changed = false;
}
}
bp.protectionLevel = fixedLevel;
info = new PermissionInfo(info);
info.protectionLevel = fixedLevel;
bp.perm = new PackageParser.Permission(tree.perm.owner, info);
bp.perm.info.packageName = tree.perm.info.packageName;
bp.uid = tree.uid;
if (added) {
mSettings.mPermissions.put(info.name, bp);
}
if (changed) {
if (!async) {
mSettings.writeLPr();
} else {
scheduleWriteSettingsLocked();
}
}
return added;
}
這個函數首先調用checkPermissionTreeLP並傳入權限名來判斷調用app是否聲明了對應的權限樹根節點:
private BasePermission checkPermissionTreeLP(String permName) {
if (permName != null) {
BasePermission bp = findPermissionTreeLP(permName);
if (bp != null) {
if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid "
+ Binder.getCallingUid()
+ " is not allowed to add to permission tree "
+ bp.name + " owned by uid " + bp.uid);
}
}
throw new SecurityException("No permission tree found for " + permName);
}
先通過findPermissionTreeLP從mSettings.mPermissionTrees找到對應的權限樹描述對象,如果沒找到,拋出異常,如果找到了,接著通過UID來判斷調用應用和權限樹定義app是否一致,如果不一致,拋出異常。
checkPermissionTreeLP通過後,接著看對應的權限是否已經定義,如果未定義,就繼續創建DYNAMIC類型的權限並添加到權限列表,如果已經定義了,接著判斷已經存在的權限類型是否為DYNAMIC,如果不是,拋出異常,如果是,則更新權限數據。
接著看grantPermission:
@Override
public void grantPermission(String packageName, String permissionName) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
final BasePermission bp = mSettings.mPermissions.get(permissionName);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + permissionName);
}
checkGrantRevokePermissions(pkg, bp);
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
}
final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
if (gp.grantedPermissions.add(permissionName)) {
if (ps.haveGids) {
gp.gids = appendInts(gp.gids, bp.gids);
}
mSettings.writeLPr();
}
}
}
從代碼可以看出,要想成功grant,必須滿足:
1) packageName對應的package要存在
2) permissionName對應的權限要已經添加到系統權限列表
3) packageName對應的package要聲明request這個權限
4) 只有權限保護等級為normal或dangerous,並且在對應的package內還未被required的權限允許grant,這個基本也就DYNAMIC permission能滿足了
以上四點必須全部滿足,否則就會拋出異常
接著將permission name添加到grantedPermissions,然後append對應的supplementary GID。
2.3.3.4權限查詢
App在調用某些系統函數的時候,函數的開頭會檢查app是否擁有對應的權限,比如:
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
這個調用繞一大圈,最終還是通過調用PMS.checkPermission來判斷調用app是否擁有對應的權限
@Override
public int checkPermission(String permName, String pkgName) {
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(pkgName);
if (p != null && p.mExtras != null) {
PackageSetting ps = (PackageSetting)p.mExtras;
if (ps.sharedUser != null) {
if (ps.sharedUser.grantedPermissions.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
} else if (ps.grantedPermissions.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
return PackageManager.PERMISSION_DENIED;
}
代碼很簡單,先查找pkgName對應的Package,接著拿到PackageSetting對象,然後從grantedPermissions中查找是否包含已經對應的permission來確認該權限是否被grant。
圖片效果:1 主頁代碼:MainActivity:1.首頁代碼:SyFragment:1.新聞頻道代碼:2.新聞內容代碼:跳轉——顯示新
手機qq安裝失敗怎麼辦?手機qq安裝後打不開是什麼原因?下面我們一起看看解決方法吧!手機安裝失敗原因及解決方法1、證書過期(或者未生效): 下載安裝前將手
在看android基礎的時候,關於網絡操作一般都會介紹HttpClient以及HttpConnection這兩個包。前者是apache的開源庫,後者是android自帶的
android 向服務器端發送json數據,本文講解的知識點比較基礎,如果你是大神,請直接關閉該網頁,免得浪費你寶貴時間。 1.向服務器端發送json數據 關鍵代碼: