IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android无法接收到广播权限问题分析 -> 正文阅读

[移动开发]Android无法接收到广播权限问题分析

最新碰到了一个问题,由于发送了一个复位的广播:权限为:

<uses-permission android:name="android.permission.MASTER_CLEAR" />
然后并未接收到复位的这条广播,根据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) {
            /*
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
                    here);
            */
            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<String> 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<String> 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<sysPs.pkg.requestedPermissions.size(); 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中.
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:34:46  更:2022-07-17 16:37:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 3:59:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码