Android权限机制(三) 针对权限控制如何设计App

随着Android 5.0的到来,原生的权限管理控制功能AppOps终于登场(虽然4.3的代码中已经包含)。

它的使用路径是Settings -> Security -> AppOps(有些厂商的ROM可能依然屏蔽着,或名字被修改)

AppOps引进的一个新概念"Ops",也就是"Operations"操作。
之前的第三方应用(如LBE)和手机厂商ROM(三星、MIUI),都实现了类似功能,但强调的是permission权限。
而在AppOps中,ops操作是映射到permission的(一般是1:1,但不是所有op都有对应的permission)
(见AppOpsManager.java的sOpToSwitch和sOpPerms数组)

在经历了工信部新版本的入网信息安全标准,以及各大运营商越来越详细的安全功能要求之后,
像AppOps这种权限/操作控制功能(以及更底层的SELinux/SEAndroid)将会是标配。
所以,我们来了解一下,开发者如何针对这些控制,设计开发出稳定的App。

 

1. 假定app用到的权限会被禁用
开发过程中,当调用到需要权限的API时候,一定要考虑两个问题:“这个API调用一定会成功吗?”“如果调用失败该怎么处理?”
权限控制的方法有很多,比如《Android权限机制(二) 权限控制的设计》中提到的,通过让checkPermission()返回false,这会导致Framework层抛出SecurityException。
所以要try/catch来处理,防止App因此crash。

ContentResolver resolver = getContentResolver();
try {
	Cursor cur = resolver.query( 
			ContactsContract.Contacts.CONTENT_URI, 
			null, 
			null, 
			null, 
			ContactsContract.Contacts.DISPLAY_NAME 
					+ " COLLATE LOCALIZED ASC");
} catch (SecurityException e) {
	Log.e("query", "permission denied");
}


另外一种控制方法就如AppOps、CyanogenMod或XPosed,在调用此类API的时候返回空/假数据,所以调用完API要检查得到的结果是否有效。

LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

boolean isNetworkEnabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
Log.i("test", "isNetworkEnabled = " + isNetworkEnabled);

if (isNetworkEnabled) {
	Location location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
	Log.i("test", "location = " + location);
}

 

//权限被阻拦的log:
20:09:45.985: I/test(30816): isNetworkEnabled = true
20:09:45.985: I/test(30816): location = null
//权限不被阻拦的log:
20:12:07.955: I/test(31132): isNetworkEnabled = true
20:12:07.955: I/test(31132): location = Location[network *,* acc=104 et=+46m11s701ms]


2. 减少不必要的权限申请
权限的控制,其实就是涉及到的就是manifest中申请的权限,以及运行中调用需要权限的API。
可能有人会问,不申请这些权限,怎么达到我想要的功能?
其实有很多种方法,比如参考《你的Android应用完全不需要那么多的权限》。把需要权限的操作交给系统app去做,是个稳妥的选择。
更有创意的,参考最近知乎一条很有意思的提问,《搜狗号码通 lite 是如何在不越狱的情况下判定骚扰电话的?》。绕开权限控制的方法有很多。

3. 其他的权限控制方法
比如"android.permission.INTERNET"之类的底层权限,并不是在调用API做的检查,而是通过将进程添加进inet用户组来达到允许联网的效果。(/system/netd/include/Permisson.h, /system/netd/server/NetworkController.cpp)。
这就是为什么没有添加这个权限,在Java或NDK下都联网失败的原因。

还有就是在4.3引入,在4.4中强制打开的SELinux/SEAndroid。

虽然SELinux在桌面Linux上被很多人吐槽,但是在服务器和Android中,却运行得很好。
简单来说,移植到Android的SELinux(SEAndroid)是在Linux userspace和kernel共同完成的MAC控制的安全功能,
它可以由管理员/手机厂商的push来配置sepolicy(安全策略)。
常见的由SELinux阻拦导致的错误可以是open()的时候被permission denied。这往往是尝试打开某些系统目录下文件导致的。

总结
在编写App(包括NDK)的时候,一定要有权限被禁用的意识,多考虑可能因此发生的情况。
此外,要知道不同手机厂商ROM对于权限控制功能的实现以及对于sepolicy的配置不尽相同。
有可能这个手机上运行成功,那个手机上就出错了。
因此,多买些手机,多换几个版本,做详尽的测试吧!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。