Android重置权限后应用重启问题研究|社区征文

社区征文Android
背景

测试那边反馈了一个bug:把软件切换到后台,把已经授予的存储权限关掉,再切回到demo,demo就像崩溃了一样,重新初始化,并且清除了之前的登录状态,详情可参考录屏:

ezgif.com-gif-maker (1).gif

定位

根据录屏可以看出,在设置中重置了权限后再返回app,app会再次进行初始化,根据logcat也可以看出对应的进程号也变了。根据该现象,可以确定与权限相关。

根据反馈,出现问题的手机版本是Android11。为了确定该问题是否在部分手机或系统上才会出现,我用华为(鸿蒙系统)以及三星(Android10)尝试复现,均复现成功。由此可以得出,该问题并不是因为Android11带来的适配问题。

最初,通过logcat并没有发现相关的报错信息,所以,我们可以初步排除是代码问题。

为了验证这个问题是否是我们应用特有情况,我用手机测试了微信以及另外一款轻量级的app,发现都出现了重启情况。

至此,问题似乎有点明了。我们可以大胆猜测,这个问题应该是Android权限的一种内部机制。

我们可以先猜测一下,Android为什么要这么设计?

首先,如果我们把权限由禁止改成允许,app不会重启,这个其实符合预期。

怎么理解?

默认情况下我们肯定是希望manifest文件中的所有权限都是授予的,毕竟开发的功能如果跟权限紧密相关,那也就意味着如果有了权限,对应的功能就可以使用,这个其实是符合开发者的意图。但是为了安全起见,谷歌在6.0加入了动态权限的设计。

其次,如果我们把权限由允许改成禁止,app会重启。这个怎么理解?首先,根据动态权限设计的初衷来看,当我们需要使用的时候再申请权限,所以权限是跟功能强相关的,如果不进行重启,那么在使用到某个功能的时候,但是我们并没有权限(已经被撤回了,系统内部会把该信息同步到app内部)

那么肯定会报permission denied....这个肯定不是最优解。如果不进行重启,那么动态权限的逻辑就会滞后,并不同步(如果app正在使用某个授予了权限的功能)这个也不符合逻辑。

通过上面的初步分析,大概可以了解,权限重置为什么会重启app。

分析

Android权限分类

首先,我们需要对权限分类有个了解,在接下来的代码分析中会用到。

Android 将权限分为不同的类型,包括安装时权限运行时权限特殊权限。每种权限类型都指明了当系统授予应用该权限后,应用可以访问的受限数据范围以及应用可以执行的受限操作范围。

安装时权限

安装时权限授予应用对受限数据的受限访问权限,并允许应用执行对系统或其他应用只有最低影响的受限操作。如果您在应用中声明了安装时权限,系统会在用户安装您的应用时自动授予应用相应权限。应用商店会在用户查看应用详情页面时向其显示安装时权限通知,如下图所示。

image.png

图中展示的是某应用商店中显示的某个应用的安装时权限列表。

Android 提供多个安装时权限子类型,包括普通权限和签名权限。

普通权限

此类权限允许访问超出应用沙盒的数据和执行超出应用沙盒的操作。但是,这些数据和操作对用户隐私及对其他应用的操作带来的风险非常小。

系统会为普通权限分配“normal”保护级别,例如网络权限:"android.permission.INTERNET"。

签名权限

当应用声明了其他应用已定义的签名权限时,如果两个应用使用同一证书进行签名,系统会在安装时向前者授予该权限。否则,系统无法向前者授予该权限。

系统会为签名权限分配“signature”保护级别,例如获取电量统计数据:"android.permission.BATTERY_STATS"。

运行时权限

运行时权限也称为危险权限,此类权限授予应用对受限数据的额外访问权限,并允许应用执行对系统和其他应用具有更严重影响的受限操作。因此,您需要先在应用中请求运行时权限,然后才能访问受限数据或执行受限操作。当应用请求运行时权限时,系统会显示运行时权限提示,如下图所示:

image.png

许多运行时权限会访问用户私有数据,这是一种特殊的受限数据,其中包含可能比较敏感的信息。例如,位置信息和联系信息就属于用户私人数据。

特殊权限

特殊权限与特定的应用操作相对应。只有平台和原始设备制造商 (OEM) 可以定义特殊权限。此外,如果平台和 OEM 想要防止有人执行功能特别强大的操作(例如通过其他应用绘图),通常会定义特殊权限。

系统设置中的特殊应用访问权限页面包含一组用户可切换的操作。其中的许多操作都以特殊权限的形式实现。例如查询正在进行的通话详情和管理正在进行的通话:"android.permission.MANAGE_ONGOING_CALLS"

以上就是Android官方对权限的分类,详细的权限列表可参考网址: https://developer.android.com/reference/android/Manifest.permission

源码分析

刚刚已经讲过,导致app重启的问题是权限重置的问题,那我们只要找到相关的源码即可,首先我们可以定位和permission相关的类,然后再通过回调的逻辑找到具体的代码(设置中把权限重置,app重启,这个逻辑中肯定会存在回调的逻辑)。

首先我在 https://cs.android.com/ 中搜索了关键字:permission,根据结果找到了一个权限管理类:PermissionManagerService。

经过查找,我们发现了PermissionCallback,根据名字就可以看出是处理权限回调的。

private PermissionCallback mDefaultPermissionCallback = new PermissionCallback() {

        @Override

        public void onGidsChanged(int appId, int userId) {

            mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
        }

        @Override

        public void onPermissionGranted(int uid, int userId) {

            mOnPermissionChangeListeners.onPermissionsChanged(uid);

            // Not critical; if this is lost, the application has to request again.

            mPackageManagerInt.writeSettings(true);

        }

        @Override

        public void onInstallPermissionGranted() {

            mPackageManagerInt.writeSettings(true);

        }

        @Override

        public void onPermissionRevoked(int uid, int userId, String reason) {

            mOnPermissionChangeListeners.onPermissionsChanged(uid);

            // Critical; after this call the application should never have the permission

            mPackageManagerInt.writeSettings(false);

            final int appId = UserHandle.getAppId(uid);

            if (reason == null) {

                mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));

            } else {
                mHandler.post(() -> killUid(appId, userId, reason));
            }

        }

        @Override

        public void onInstallPermissionRevoked() {
            mPackageManagerInt.writeSettings(true);
        }

        @Override

        public void onPermissionUpdated(int[] userIds, boolean sync) {
            mPackageManagerInt.writePermissionSettings(userIds, !sync);
        }

        @Override

        public void onInstallPermissionUpdated() {
            mPackageManagerInt.writeSettings(true);

        }

        @Override

        public void onPermissionRemoved() {
            mPackageManagerInt.writeSettings(false);
        }

        public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,
                int uid) {
            onPermissionUpdated(updatedUserIds, sync);
            for (int i = 0; i < updatedUserIds.length; i++) {
                int userUid = UserHandle.getUid(updatedUserIds[i], UserHandle.getAppId(uid));             mOnPermissionChangeListeners.onPermissionsChanged(userUid);
            }

        }

        public void onInstallPermissionUpdatedNotifyListener(int uid) {
            onInstallPermissionUpdated();
          mOnPermissionChangeListeners.onPermissionsChanged(uid);
        }

    };

我们主要看onPermissionRevoked()这个回调:

image.png

该方法会调用killUid()方法:

image.png

根据注释可以看出,当权限被授予或被重置时,app会立即重启。

结论

通过对问题的定位与分析,以及结合源码的分析,我们可以得出:当我们对app的权限进行重置操作时,希望会对app进行重新初始化操作,该逻辑为Android权限机制。

最后,我们还测试了一下iOS系统,发现在iOS上如果在设置里对某个应用的权限做重置操作,也会导致该应用重启。

从Android近几个大版本的迭代中,我们可以明显的注意到,Android对权限这一块的把控越来越严。一方面是为了将Android打造成最安全的操作系统,另外一方面也是对用户隐私的重视。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论