Android9.0动态运行时权限源码分析及封装改造<一>-----运行时权限名词解释、权限检测源码分析
概述:
我们都知道Google在Android6.0时引入了权限申请机制,也是变革非常大的一块,而如今商用项目中基本上都会涉及到这块,基本上都是使用三方成熟的框架来处理权限的问题,最常用的框为https://github.com/googlesamples/easypermissions,但是!!对于权限这块的知识有没有认真的去研究过呢?反正目前每次想到权限这块我的头还是有点大的,这也就是没有掌握这块原因所致,所以作为一个有追求的码农必须花时间给好好研究一下这块的机理,所以才产生写此篇博客的想法,虽说是烂大街的技术,但是只要是自己没掌握的就得学!
运行时权限名词解释:
先来了解一些跟权限相关的名词,了解它们能够更好的理解接下来要研究的东东,因为分析权限框架源码时会涉及到。
什么是uid、pid?
- uid:本身是linux权限系统中用以区分用户身份的标识,由于android是单一用户权限系统,uid在android里面又可以理解为应用的标识Id,该Id自安装之日起就被分配,始终如一。
- pid:顾名思义是进程唯一标识id,用以和远程服务交互。
什么是appId、callerId?
- appId:可以理解为应用Id,跟uid一样,这是本地应用在远程服务中的叫法。
- callerId:它是服务请求者的身份Id,可以是本地应用的身份Id,也可以是远程服务在身份标识Id。
危险权限(组):
Android6.0以后将系统权限分为两类:正常权限和危险权限, 对于这俩权限看一下它们俩的区别,借网上的说明:
动态权限处理指的就是对于危险权限的处理,下面来了解一下危险权限都有哪一些:
权限检测源码分析【Android9.0】:
接下来则来分析Android9.0权限框架的源码,分析的入口则是从平常咱们写的申请权限的调用代码开始,先来回忆一下我们是如何来调用的:
package com.permissionarchstudy.test; import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; public class MainActivity extends AppCompatActivity { private static final int RESULT_CODE_PERMISSIONS_WRITE_EXTERNAL_STORAGE = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RESULT_CODE_PERMISSIONS_WRITE_EXTERNAL_STORAGE); } } } }
其中申请权限涉及到上面标红的三个api,接下来则按着我们调用的顺序来分析。
checkSelfPermission:
具体来分析一下它:
接下来则点击进去瞅一瞅,我们知道am的具体实现类是ActivityManagerService,所以咱们往它里面继续定位:
继续,发现无法点进去了:
如何解决IDE中跳不到隐藏API的问题?
其实我们手动打开ActivityManager类是能找到这个方法的,如下:
其实将这个hide注解去掉就可以了,问题是我们SDK是从官网下的,去不掉呀,其实网上github上已经有人帮我们整理了,地址:https://github.com/anggrayudi/android-hidden-api:
下面咱们来试一下,下载android-28既可,因为正好咱们分析的就是这个版本:
替换到这个位置:
此时IDE刷新之后,就可以正常的跳转啦,如下:
这样看代码就比较方便了,学了个小技巧,流程继续往下:
那啥叫隔离进程呢?在Android中其实是有一个进程的范围的,比如有些应用被拉入黑名单,总之这个进程是没有任何权限的,继续往下:
那又反问一下,啥叫应用自己的权限,我们知道我们可以在manifest中声明自己的权限,比如:
我们知道在manifest中是可以加export属性的:
像上面如果这样声明是不允许被其它调用的,比较好理解,继续:
咱们来瞅一下它的细节:
而我们知道它的具体Service则是PackageManagerService,跟进去看一下:
所以,到这个类中来查看进一步的细节:
这个是指访问者或者申请者的应用ID,这个一般是给Google小程序来使用的,国内APP是用不到的,继续:
这里了解一下https://blog.csdn.net/singwhatiwanna/article/details/80490124,其实就是类似于微信小程序:
对于我们应用来说肯定是用不到了,了解一下既可,继续:
正常我们平常开发的应用是不会要配置它的,所以这个条件肯定是进不来的,则看其它条件:
以上条件都没有通过,那很显然此权限木有拥有,则直接返回未授权了:
下面用一张图对上面整个流程进行一个梳理:
shouldShowRequestPermissionRationale:
在上面中如果检测到该权限木有授权,则会往下来执行到它:
这是干嘛的呢?看一下官方的解释:
啥意思?继续往下读一段你就明白了,官方举了一个具体的使用它的场景:
哦,这样哟,往里分析一下它的细节:
而它最终会跳到这:
一看就跳到了PackageManagerService了,如下:
回到主流程继续往下分析:
其中FLAG_PERMISSION_USER_FIXED它是指用户在权限中明确设置了“不再询问”了,最后就是判断一下是否是这个FLAG,如果是则就需要显示弹窗:
这样的弹窗大概长这样子:
下面也同样来总结一下上面的流程: