我们正在使用新的AppOpsManager.OnOpNotedCallback
. 我们遇到的问题是一些外部库自己获取某些操作的上下文,这意味着我们没有得到任何attributionTag
指定。另一个问题是,对于onAsyncNoted
回调(这是我们实际上得到的全部),堆栈跟踪对于尝试解决关键区域访问的来源没有帮助。
这种堆栈跟踪的示例:
E/CriticalAreaAccessAuditer: Private data accessed. Operation: android:record_audio; Attribution tag: null; Message: Audio recording on session 409; Stack Trace:
net.example.criticalareaaudit.CriticalAreaAccessAuditer$initialize$appOpsCallback$2$1.onAsyncNoted(CriticalAreaAccessAuditer.kt:59)
android.app.AppOpsManager$OnOpNotedCallback$1.lambda$opNoted$0$AppOpsManager$OnOpNotedCallback$1(AppOpsManager.java:9342)
android.app.AppOpsManager$OnOpNotedCallback$1$$ExternalSyntheticLambda0.run(Unknown Source:4)
android.os.Handler.handleCallback(Handler.java:938)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loopOnce(Looper.java:201)
android.os.Looper.loop(Looper.java:288)
android.app.ActivityThread.main(ActivityThread.java:7796)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:974)
我们的onAsyncNoted
代码:
override fun onNoted(op: SyncNotedAppOp) {
logPrivateDataAccess(op.op, op.attributionTag, Throwable().stackTrace.joinToString("\n").trim())
}
override fun onSelfNoted(op: SyncNotedAppOp) {
logPrivateDataAccess(op.op, op.attributionTag, Throwable().stackTrace.joinToString("\n").trim())
}
override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
logPrivateDataAccess(asyncOp.op, asyncOp.attributionTag, Throwable().stackTrace.joinToString("\n").trim(), asyncOp.message)
}
private fun logPrivateDataAccess(operation: String, attributionTag: String?, trace: String, message: String? = null) {
Timber.e("Private data accessed. Operation: $operation; Attribution tag: $attributionTag; Message: $message; Stack Trace:\n$trace")
}
我们注意到的另一件事是,对于关键区域操作的自我注释,attributionTag 和 message 是空的,即使它们已设置并且调用返回AppOpsManager.MODE_ALLOWED
:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
appOpsManager.noteOp(AppOpsManager.OPSTR_RECORD_AUDIO, myUid(), packageName = "net.example",
attributionTag = "SomeManager", message = "SomeManager.doThing()")
}
它产生了这个输出(至少在这里我们可以帮助我们自己使用stackTrace,但是依赖attributionTag 仍然会更好,因为我们已经设置了它):
E/CriticalAreaAccessAuditer: Private data accessed. Operation: android:record_audio; Attribution tag: null; Message: null; Stack Trace:
E/CriticalAreaAccessAuditer: net.example.criticalareaaudit.CriticalAreaAccessAuditer$initialize$appOpsCallback$2$1.onSelfNoted(CriticalAreaAccessAuditer.kt:58)
E/CriticalAreaAccessAuditer: android.app.AppOpsManager.collectNotedOpForSelf(AppOpsManager.java:8943)
E/CriticalAreaAccessAuditer: android.app.AppOpsManager.noteOpNoThrow(AppOpsManager.java:8150)
E/CriticalAreaAccessAuditer: android.app.AppOpsManager.noteOp(AppOpsManager.java:8087)
E/CriticalAreaAccessAuditer: android.app.AppOpsManager.noteOp(AppOpsManager.java:8077)
E/CriticalAreaAccessAuditer: net.example.manager.SomeManager$doThing$2.apply(SomeManager.kt:96)
E/CriticalAreaAccessAuditer: net.example.manager.SomeManager$doThing$2.apply(SomeManager.kt:31)
归根结底,问题是,如何有效地使用AppOpsManager.OnOpNotedCallback
外部库来审核和区分允许和不允许的关键区域访问。目前,看起来它AppOpsManager.OnOpNotedCallback
并不能真正胜任大型复杂应用程序的任务。