所谓权限管理,就是能够手动配置某个 App 的权限,进而阻止恶意软件以及防止隐私泄漏。当然,更进一步的权限管理,是能够在 App 动态使用某个权限的时候,弹窗提示用户允许和拒绝。这样的权限管理就更加类似于 Windows 中的主动防御。
App Ops 的基本架构如图所示:
其中包含两个重要部分,一个叫做AppOpsService,另外一个叫AppOpsManager。AppOpsService 是一个系统服务,注册的名字叫做 “appops”,由ActivityManagerService启动,启动后会调用 readWhitelist();读取预设的白名单,接下来读取data/system/appops.xml文件,解析里面的元素并存放到一个SparseArray
AppOpsManager 是一个访问 AppOps 服务的类,同时有 Java 和 C 的实现,为了应对某些 native code 的服务,比如说 Camera。Settings 可以使用 AppOpsManger 来读取和修改权限管理信息。当其他 App 使用某个权限的时候,会通过 Binder 访问服务端的某项服务。在服务端的各个服务中都插入了检查权限的代码,同样通过使用 AppOpsManger 来检查权限。
2.2.1
检查用户设定权限的函数是:checkOperation() 和 noteOperation(),区别是 checkOperation() 只是检查 Operation 的情况,noteOperation() 还会记录访问时间等信息,代码如下:
public int checkOperation(int code, intuid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
synchronized (this) {
Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName,false);
if (op == null) {
return AppOpsManager.MODE_ALLOWED;
}
return op.mode;
}
}
public int noteOperation(int code, int uid,String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code+ " uid " + uid
+ " package " +packageName);
return AppOpsManager.MODE_IGNORED;
}
Op op = getOpLocked(ops, code, true);
if (op.duration == -1) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg" + packageName
+ " code " + code+ " time=" + op.time + " duration=" + op.duration);
}
op.duration = 0;
final int switchCode = AppOpsManager.opToSwitch(code);
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode,true) : op;
if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode +" for code "
+ switchCode + "(" + code + ") uid " + uid + " package " + packageName);
op.rejectTime = System.currentTimeMillis();
return switchOp.mode;
}
if (DEBUG) Log.d(TAG, "noteOperation:allowing code " + code + " uid " + uid
+ " package " +packageName);
op.time = System.currentTimeMillis();
op.rejectTime = 0;
return AppOpsManager.MODE_ALLOWED;
}
}
修改某个 App 的某项权限的函数是 setMode(),其中就是修改成员变量 mUidOps。mUidOps 是一个List 保存了某个package对应的所有权限的mode (允许,忽略),
修改 mode 之后会通过 writeState() 的函数,最终写入文件当中,他用 appops.xml 来存储 App Ops 信息,如果想了解 xml 中的各个 tag 的语义,可以查看 writeState() 函数的实现。 简单来说他会记录 uid, 包名,mode,访问时间,拒绝时间。
2.2.2AppOpsManager
AppOpsManager是一个管理类来和AppOpsService 通信,他的函数实现比较简单,重点是把控制转移到 AppOpsService。例如 noteOperation() 和 setMode() 在 AppOpsManager 里面调用他们的函数是 noteOp() 和 setMode()。
2.2.3 拦截代码
有了 noteOp() 函数,但是要完成权限的动态检查,还要在执行某项权限的时候执行 noteOp()。经过分析,大概有十多个服务被插入了权限检查函数。其中包括:
ClipboardService, VibratorService,LocationManagerService, NotificationManagerService, GeofenceManager,GpsLocationProvider, IccSmsInterfaceManager, PhoneInterfaceManager,OutgoingCallBroadcaster, WifiService, ContentProvider, WindowManagerService 等等。
选取短信相关权限进行分析,看看 App Ops 究竟是如何进行权限控制的。首先了解一下发短信的流程,发短信的代码很简单,主要分两个步骤:
SmsManager smsManager=SmsManager.getDefault();
smsManager.sendTextMessage("dest","src", "content", null, null);
第一步是通过 Binder 获取 Server 段的发送短信服务 isms service。第二部是调用远端的sendTextMessage() 函数。通过 Proxy Pattern,最后到达实现的方法,位于IccSmsInterfaceManager 中的 sendText()方法,先看一下代码:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@Override
public void sendText(String callingPackage,String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mPhone.getContext().enforceCallingPermission(
Manifest.permission.SEND_SMS,
"Sending SMS message");
if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" +scAddr +
" text='"+ text + "' sentIntent=" +
sentIntent + " deliveryIntent=" + deliveryIntent);
}
if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return;
}
mDispatcher.sendText(destAddr, scAddr, text, sentIntent,deliveryIntent);
}
远端 sendText() 函数首先通过 enforceCallingPermission() 函数来检查 App 是否在 AndroidManifest.xml 中申请了 android.permission.SEND_SMS 的权限。然后在通过调用 mAppOps (AppOpsService 的服务端实例 AppOpsManager) 调用 noteOp() 函数检查是否通过了用户的权限设置,如果用户选择拒绝或者忽略,就直接 return。
权限管理的功能,4.3代码还未完善,但是思路和框架设计的比较好,可能不会引出什么新的漏洞。Google 在迎合大众的需要的同时,故意放出了一个半成品,来测试一下大家的反映。4.4已基本完善。5.0的代码还没来得及分析,原理应该是一样的。