Android dynamic permission

来源:互联网 发布:自助发稿平台 源码 编辑:程序博客网 时间:2024/05/17 03:55

Android dynamic permission

ContentProvider Uri permission

  • Android dynamic permission
    • ContentProvider Uri permission
    • 前言
    • 1ContentProvider静态授权
    • 2ContentProvider 动态授权
      • 1Intent动态授权的开始
      • 2Intent动态授权的检查
      • 3Intent动态授权的过程
      • 4Intent动态授权的结果
      • 5Intent动态授权的结束

前言

Android为每个应用程序分配一个唯一的UID,以确保每个应用程序有独立的空间,使它与其它应用程序分隔开来,避免其他应用程序进行非授权的访问。Android可以为其几大组件定义permission,包括Activity,Service,ContentProvider,Broadcast。对于Application,Android开发者最为熟悉,在AndroidManifest.xml中声明权限,应用就拥有了该权限,当然对于Android 6.0以后的版本,risk permissions还需要遵行Android Runtime permission机制。本文将对Content-Provider的权限进行简单的说明,并对动态permission进行深入的探讨。Dynamic permission在android系统中很常见,是app之间进行数据安全共享发挥了举足轻重的作用。本文更适合具备良好的android四大组件的基础、具备android server(ActivityManagerService)基本的认识、具备android binder基本认识的读者阅读。

1、ContentProvider静态授权

首先回顾一下ContentProvider的静态授权机制,以便更好理解后文dynamic permission机制。Android ContentProvider静态注册权限,类似app的在AndroidManifest.xml文件中声明,ContentProvider声明权限如下:

<provider android:name=“provider package nameandroid:authorities=“authorities”  android:readPermission=“com.android.read.dynamic.permisssion”  android:writePermission=“com.android.write.dynamic.permisssion”  android:grantUriPermission=“true”>  </provider> 

很简单,上面的provider就授权整个provider的权限给其他应用。我们还可以只授权其中的某个子路径给其他应用。

<provider android:name=“provider package nameandroid:authorities=“authorities”  android:readPermission=“com.android.read.dynamic.permisssion”  android:writePermission=“com.android.write.dynamic.permisssion”>  <grant-uri-permission android:path=/attachments/”>  </provider>  

这样授权以后,别的应用就可以随时访问该ContentProvider。但是有时APP的内容并不想这样随时让任何应用都可以通过该ContentProvider获取数据,有读者就可以想到,ContentProvider指定授权给目标APP。这样,仅有被ContentProvider指定授权的APP才有权限操作ContentProvider的内容和数据。

public void grantUriPermission(String  toPackage, Uri uri, int modeFlags) {         ……}

如上面的code,只有toPackage的APP才有权限操作指定Uri的内容和数据,似乎功能已经很强大,安全性也比较好。但是,这种方式的缺点也是很明显,第一灵活性非常低,code一旦写死,就无法改变;第二局限性非常强,只有指定的package才能访问Content-Provider;第三主动性过于强烈,只有ContentProvider主动授权,APP才有权限访问。因为有这样的缺点,无法满足以下需求:1、ContentProvider无法确定目标APP的package的情况,就是Content¬Provider的无法知道需要访问数据是“谁”的情况;2、按需授权权限给APP,即“谁”需要访问内容和数据,就授权指定权限给固定的APP。

2、ContentProvider 动态授权

在上文中静态授权和通过grantUriPermission动态授权具有很多缺点,无法满足android系统的开放性和数据共享的各种需求。于是,动态Intent授权便发挥了巨大的作用。这个在android系统当中非常常见,在常用的分享功能,从一个APP分享内容到其它APP,就处处用到动态授权Uri。例如,从图库分享一张照片,往往系统会弹出多个匹配的应用可选择(如QQ,微信,SMS等等),如下图:

这里写图片描述

这时,ContentProvider只是动态给使用设备的用户选择的APP进行授权,这个过程跟AndroidManifest.xml中声明权限和通过接口grantUriPermission授权的方式不同。这种就是Intent动态授权Uri权限,下文,将会对这种机制的过程详细赘述。

2.1、Intent动态授权的开始

如上文中图1,当用户选择了某应用,如Messaging,便会启动Messaging应用,所以,读者也可以想象到,从启动应用便做了授权的动作。先回忆一下如何启动这个应用,如Messaging。熟悉SMS开发的读者很快就会想到通过SMS的Action,如下

public static final String ACTION_SEND = "android.intent.action.SEND";

有了这个Action,还需要封装一下intent,就是让目标应用得到共享数据的信息,如让Messaging知道在那个地方获取什么内容和数据,也就是Uri。Intent设置Uri的方式如下

public Intent setData(Uri data) {    ……}public void setClipData(ClipData clip) {    ……}

这个很关键,在Intent动态授权的依据就是intent所携带的Uri。Intent都准备好后,后面自然就是启动Activity了,通常,如下是android启动activity的方式

public abstract void startActivity(Intent intent);

调用这个方法以后,和资源提供者(ContentProvider)没有关系了,接着往下分析。对android四大组件管理熟悉的读者,现在会联想到ActivityManagerService,没错,startActivity之后,会跑到Activity-ManagerService,最终会调用ActivityManagerService的如下方法

void grantUriPermissionFromIntentLocked(int callingUid, String targetPkg,…     ……}

从启动activity和解析intent进行Uri动态授权,在这个方法里面,需要重点关注callingUid和targetPkg,即ContentProvider的用户id和被授权的目标应用的package,这两个参数在intent动态授权Uri的安全和合理性上很重要,读者读完下文就会有这样的感受。

2.2、Intent动态授权的检查

检查这个步骤主要发挥的就是合理性和安全性检查,实现代码如下

NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,            String targetPkg, Intent intent, int mode,…){……targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,                    targetUid);……}

上面的代码可以过滤一些不合理或非法的授权,如某应用给自己授权别的应用的Uri获取内容和数据,如Messaging是不能自己给自己授权获取图1中图片的权限。此时callingUid和targetPkg便发挥了很重要的作用。关于更多检查的过程,本文就不再赘述。

2.3、Intent动态授权的过程

在上文中intent动态授权的检查通过后,便会进入真正的授权过程,这个过程在如下方法中完成

void grantUriPermissionUncheckedFromIntentLocked(NeededUriGrants needed,            UriPermissionOwner owner) {        ……       grantUriPermissionUncheckedLocked(needed.targetUid,           needed.targetPkg, grantUri, needed.flags, owner);}

Needed来自checkGrantUriPermissionFromIntentLocked方法,封装了intent携带过来的数据,继续往下跟踪代码

private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,            String targetPkg, int targetUid, GrantUri grantUri) {        if (targetUris == null) {            targetUris = Maps.newArrayMap();            mGrantedUriPermissions.put(targetUid, targetUris);        }        ……        return perm;}

这里关注mGrantedUriPermissions.put(targetUid, target¬Uris)这条语句,mGrantedUriPermissions是ActivityManger¬Service的全局变量,是一个SparseArray类型的实例对象,动态intent授权Uri便是把Uri以targetUid作为key保存在mGrantedUriPermissions中,那么在targetPkg获取数据时系统便会根据targetUid来判断该应用有没有被授权。

2.4、Intent动态授权的结果

在启动activity的时候完成了Uri动态授权的操作,那么,有授权,必定有权限性检查的步骤,例如图1中的图片分享,选择Messaging分享照片,而Messaging便会根据intent携带的Uri去加载照片数据,在Messaging主动加载图片时,系统便会对Messaging的这一行为的权限进行检查,如下所示

public int checkUriPermission(Uri uri, int pid, int uid,            final int modeFlags, int userId, IBinder callerToken) {     ……     synchronized (this) {     return checkUriPermissionLocked(new GrantUri(userId, uri, false), uid,      modeFlags)                    ? PackageManager.PERMISSION_GRANTED                    : PackageManager.PERMISSION_DENIED;        }}

在这个方法里面,有两个参数非常值得关注,第一Uri uri,这个便是被授权的Uri,即如图1中的例子,便是照片的Uri;第二是int uid,即需要获取内容和数据的APP,如图1中的Messaging。继续跟踪方法checkUriPermissionLocked()

private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,            final int modeFlags) {    ……    final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);    if (perms == null) return false;    ……    for (int i = 0; i < N; i++) {       final UriPermission perm = perms.valueAt(i);       if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)                    && perm.getStrength(modeFlags) >= minStrength) {            return true;        }    }    return false;}

mGrantedUriPermissions.get(uid);,从对象mGrantedUri-Permissions中根据uid获取封装了Uri的ArrayMap的类型的实例对象perms,mGrantedUriPermissions正是上文提到的保存授权Uri的SparseArray的类型的实例化对象,uid便是上文中提到的targetUid是相同的值。也就是说,在mGrantedUriPermissions中根据uid作为key能够获取到Uri,便是已授权,即Package-Manager.PERMISSION_GRANTED。

2.5、Intent动态授权的结束

既然是动态授权,有开始授权,便会有结束授权。Intent动态授权开始于startActivity(),结束于Activity的destroy,如下所示

void removeUriPermissionIfNeededLocked(UriPermission perm) {   if (perm.modeFlags == 0) {     final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(                    perm.targetUid);     ……     mGrantedUriPermissions.remove(perm.targetUid); }

Intent动态授权Uri permission的生命周期起于启动activity,结束于activity的销毁。因此,目标应用要在启动的activity销毁之前加载源程序授权的Uri的内容和数据。

1 0