Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android PackageManager Service詳解(5.1源碼)(三)

Android PackageManager Service詳解(5.1源碼)(三)

編輯:關於Android編程

2:PackageManagerService

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中,供系統運行時使用。

2.1 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 列表

 

2.2 初始化SystemConfig

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" />

2.3 APP權限管理

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才能使用該系統功能。

 

2.3.1 生成UID和GID

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)時被更新。

 

2.3.2 Supplementary GIDS

主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 權限數據管理

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 permConfig

= 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,

"", sa,

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,

"", sa,

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] = " name has less than three segments: "

+ 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 permissionMap =

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 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) {

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 not supported

 

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 entries";

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 grantedPermissions = new 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 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) {

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 origPermissions = gp.grantedPermissions;

boolean changedPermission = false;

 

if (replace) {

ps.permissionsFixed = false;

if (gp == ps) {

origPermissions = new ArraySet(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

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 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;

}

先拿到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. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved