最新碰到了一个问题,由于发送了一个复位的广播:权限为:
然后并未接收到复位的这条广播,根据logcat打印发现:
W/BroadcastQueue( 2346): Permission Denial:
broadcasting Intent { act=android.intent.action.MASTER_CLEAR flg=0x10 }
from com.bestv.ott (pid=2593, uid=10006)
requires android.permission.MASTER_CLEAR due to receiver android/com.android.server.MasterClearReceiver
从日志分析上看是因为没有在AndroidManifest.xml中添加权限导致的,但代码中已添加以上权限。
解决方法:
由于FunTVLauncher.apk是在system/app目录下导致的。然后使用mv命令移动到system/priv-app/目录下可以了。那么为什么会导致这种情况。
问题开始分析(以下源码是Android4.4):
从日志可以看出,是在BroadcastQueue.java这个类中找到输出异常日志,从以下代码看出首先会进行权限检测,那么问题就出现在权限检测未通过,那么继续往里面找,链进去找到mService.checkComponentPermission方法
private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered) {
boolean skip = false;
if (filter.requiredPermission != null) {
int perm = mService.checkComponentPermission(filter.requiredPermission,
r.callingPid, r.callingUid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
Slog.w(TAG, "Permission Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
+ r.callingPid + ", uid=" + r.callingUid + ")"
+ " requires " + filter.requiredPermission
+ " due to registered receiver " + filter);
skip = true;
}
}
//下面代码省略........
}
mService就是ActivityManagerService,找到ActivityManagerService.java -> checkComponentPermission方法继续进行权限的检测
int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
// We might be performing an operation on behalf of an indirect binder
// invocation, e.g. via {@link #openContentUri}. Check and adjust the
// client identity accordingly before proceeding.
Identity tlsIdentity = sCallerIdentity.get();
if (tlsIdentity != null) {
Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
+ tlsIdentity.pid + "," + tlsIdentity.uid + "}");
uid = tlsIdentity.uid;
pid = tlsIdentity.pid;
}
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
接着进入到 ActivityManager.checkComponentPermission;
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;
}
通过AppGlobals进入到PackageManagerService中继续进行权限检查。AppGlobals.getPackageManager()是通过 ActivityThread.getPackageManager()来获取到IPackageManager实例,AIDL来进行跨进程通信。就回调用到PackageManagerService.java -> checkUidPermission
由一下代码可以发现我们传入进去的是权限的名字,那么grantedPermissions是一个HashSet,可以看出来传入的android.permission.MASTER_CLEAR"这个权限没有在HashSet集合中,由此要找到grantedPermissions.add数据的方法.
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 perms = mSystemPermissions.get(uid);
if (perms != null && perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
return PackageManager.PERMISSION_DENIED;
}
由一下代码看出grantedPermissions.add数据的地方是grantPermissionsLPw中,allowed这个参数就是是否允add权限,那么就要找到为什么allowed为false, 找到grantSignaturePermission(perm, pkg, bp, origPermissions)方法
private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
return;
}
//.....以上部分代码省略
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) {
}
}
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 {
Slog.w(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " because it was previously installed without");
}
}
//....以下代码省略
}
主要是isPrivilegedApp(pkg);这个函数,把apk放入system/priv-app目录下后,系统会自动的给上PARSE_IS_PRIVILEGED权限。
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
BasePermission bp, HashSet origPermissions) {
boolean allowed;
// 这里检查被安装的app的签名(pkg.mSignatures)与声明权限的app的签名(bp.packageSetting.signatures.mSignatures)是否一致,
// 如果不一致,则再检查被安装app的签名是否与系统签名(mPlatformPackage.mSignatures)一致。
// 如果其中一个是一致的,则赋予被安装app signature权限。
// 注意:只要被安装的app的签名是系统签名,则其可以访问任意第三方声明的signature权限。
allowed = (compareSignatures(
bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
Log.d("PM_DEBUG","allowed1 is " + allowed + " for pkg " + pkg.packageName + " permission is " + perm);
// 如果被安装app的签名既不是声明权限的app的签名,也不是系统签名,则继续检查其他标志位。
// 首先检查声明的权限是否是privileged权限(也就是signatureOrSystem中的System权限),如果权限是privileged的,
//那么对于系统应用(满足isSystemApp(pkg))并且是privileged应用(满足isPrivilegedApp(pkg)),就会赋予
if (!allowed && (bp.protectionLevel
& PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) {
if (isSystemApp(pkg)) {//是否是系统应用
// For updated system applications, a system permission
// is granted only if it had been defined by the original application.
Log.d("PM_DEBUG","isUpdatedSystemApp(akg) ->" + isUpdatedSystemApp(pkg) + "perm ->" + perm);
if (isUpdatedSystemApp(pkg)) {//是否是更新系统的app 比如使用adb install的app
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
final GrantedPermissions origGp = sysPs.sharedUser != null
? sysPs.sharedUser : sysPs;
if (origGp.grantedPermissions.contains(perm)) {
// If the original was granted this permission, we take
// that grant decision as read and propagate it to the
// update.
allowed = true;
Log.d("PM_DEBUG","allowed2 is " + allowed + " for pkg " + pkg.packageName + " permission is " + perm);
} else {
// The system apk may have been updated with an older
// version of the one on the data partition, but which
// granted a new system permission that it didn't have
// before. In this case we do want to allow the app to
// now get the new permission if the ancestral apk is
// privileged to get it.
if (sysPs.pkg != null && sysPs.isPrivileged()) {
for (int j=0;
j
if (perm.equals(
sysPs.pkg.requestedPermissions.get(j))) {
allowed = true;
Log.d("PM_DEBUG","allowed3 is " + allowed + " for pkg " + pkg.packageName + " permission is " + perm);
break;
}
}
}
}
} else {
// 不是更新系统app,如果被安装的app是privileged的,则赋予其权限。也就是说,
//对于/system/priv-app目录中的app,会获取到权限。这种情况发生在privileged app第一次被安装时,[或者系统被root后,
//强行push apk到system/priv-app目录?(有待验证)]。
allowed = isPrivilegedApp(pkg);
Log.d("PM_DEBUG","allowed4 is " + allowed + " for pkg " + pkg.packageName + " permission is " + perm);
}
}
}
if (!allowed && (bp.protectionLevel
& PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
// For development permissions, a development permission
// is granted only if it was already granted.
allowed = origPermissions.contains(perm);
Log.d("PM_DEBUG","allowed5 is " + allowed + " for pkg " + pkg.packageName + " permission is " + perm);
}
return allowed;
}
PackageManagerService初始化的地方给定了priv-app特殊的权限PARSE_IS_PRIVILEGED
File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
mPrivilegedInstallObserver = new AppDirObserver(
privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);
mPrivilegedInstallObserver.startWatching();
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);
总结:系统在开机的时候会在SystemServer中启动PackageManagerService服务,然后在初始化的时候会去将priv-app目录,app目录设定权限,并且解析所有apk中的清单文件并根据节点来进行存储,例如permission节点等权限存入Hashset中.
暑期编程PK赛
得CSDN机械键盘等精美礼品!