前言
看了一圈各大网站目前对于隐衷合规检测的分享,发现大家简直都是本人写一套动静代理、Hook或者ASM来实现代码拦挡从而检测不合规的代码堆栈,尽管这也是一个很好的办法,然而对于一些很紧急的工作又或者是刚入门的开发者来说,无疑是很耗时间的,所以笔者在这里提供一种目前 Android 11 反对的API思路。官网文档传送门
最近被工信部下架的利用太多了,隐衷合规检测特地严,明明把所有能看失去的隐衷合规问题都解决了,然而工信部就是说你没解决。Ok,fine! 你说没解决就没解决吧(欲哭无泪)
尽管咱们是把“看失去”的隐衷问题都解决了,然而那些看不到的呢?比如说依赖的第三方 SDK,它们会不会在你不晓得的状况下偷偷调用了一些隐衷权限呢?置信个别的公司我的项目都是比拟大型的,依赖了各种各样的第三方SDK吧(如果是大佬的话就当我没说,毕竟大佬们都喜爱本人造轮子~)
检测办法
为了让利用及其依赖项拜访用户私密数据的过程更加通明,Android 11 引入了数据拜访审核性能。
笔者应用的这种形式,是针对Android 11及以上的,因为这是Android 11的新个性,具体能够看官网文档。想要检测利用应用的隐衷权限的话,须要先临时把targetSdkVersion
升到30,而后等检测完不合规的中央后再还原回本来应用的 targetSdkVersion 版本。
其实当 Android 11 的新个性进去之后,开发者们必定或多或少的对AppOpsManager.OnOpNotedCallback
有印象,然而真到了须要用的时候,可能就想不起这个回调了,在 App 中注册了这个回调之后,当利用每次产生以下任一事件时都执行相应操作:
- 利用的代码拜访私密数据
- 依赖库或 SDK 中的代码拜访私密数据
注:此博客所讲的例子是基于定位权限的,其余隐衷权限同理。
简略应用
以下代码段是用于数据拜访时的AppOpsManager.OnOpNotedCallback
回调:
override fun onCreate(savedInstanceState: Bundle?) { val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() { private fun logPrivateDataAccess(opCode: String, trace: String) { Log.i("youzi", "Private data accessed. Operation: $opCode\n Stack Trace:\n $trace") } override fun onNoted(syncNotedAppOp: SyncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.op, Throwable().stackTrace.toString()) } override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.op, Throwable().stackTrace.toString()) } override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) { logPrivateDataAccess(asyncNotedAppOp.op, asyncNotedAppOp.message) } } // 创立AppOpsManager实例并增加下面定义的回调 val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback)}
回调里须要重写的几个办法笔者就不多赘述了,官网文档里讲得曾经很具体了。这里的logPrivateDataAccess办法是本人写的,名字可随便定义,用于打印应用到的权限名字,还有应用到的代码堆栈,不便大家看到在哪里调用了某个权限。
这里给个笔者检测时打印进去的日志示例:

能够看到,打印出的日志当中会有应用到的定位权限名字fine_location
,还有Stack Trace
上面的堆栈信息,此堆栈示意在DebugDB.initialize()
中调用了定位权限,此时咱们能够点击前面括号中的的信息跳转到该办法,而后就可以看失去此办法是属于哪个SDK的了。
(笔者悲催的发现这个 SDK 是没有中央应用的,属于被废除掉的了,如果要一个个去查所有应用到的 SDK 里是否调用了权限,那将是十分大的工作量,大家也能够趁机看一下本人我的项目里哪些不再须要的 SDK ,早删早轻松)
按归因标记应用
- 当只须要检测某个页面中是否调用了隐衷权限的话,只须要在须要检测的Activity中的onCreate注册该回调。
- 当须要在利用中检测所有页面是否调用了隐衷权限的话,须要在利用的Application中的onCreate中注册该回调。
- 当须要检测特定的权限时,能够应用
按归因标记审核数据拜访
,艰深点来说就是创立专属的Tag标记。
这里的场景一跟场景二下面曾经阐明了,区别在于在不同的中央注册而已,当初次要是要讲一下场景三,援用一下官网文档的一句话:
如果您在某个 Activity 中拜访数据(例如申请地位信息或拜访用户的联系人列表),请在该 Activity 的onCreate()
办法中调用createAttributionContext()
,并传入您心愿与利用的一部分相关联的归因标记。
举个例子,也就是说如果你须要在某个Activity中调用LocationManager
,你须要先创立一个与之关联的 Tag 标记,如下代码段所示:
private lateinit var attributionContext: Context private lateinit var locationManager: LocationManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 创立归因标记,简称Tag attributionContext = createAttributionContext("findLocation") // 通过下面的attributionContext来创立locationManager实例,关联归因标记 locationManager = attributionContext.getSystemService(LocationManager::class.java) as LocationManager }
下面能够看到咱们通过createAttributionContext("findLocation")
创立了一个 Tag,而后依据这个 context 来创立LocationManager
,在当前须要调用定位的中央就能够间接应用这个与 Tag 相关联的 locationManager 了,接下来咱们只须要略微批改一下刚刚所应用的AppOpsManager.OnOpNotedCallback
即可:
private lateinit var attributionContext: Context private lateinit var locationManager: LocationManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 创立归因标记,简称Tag attributionContext = createAttributionContext("findLocation") // 通过下面的attributionContext来创立locationManager实例,关联归因标记 locationManager = attributionContext.getSystemService(LocationManager::class.java) as LocationManager val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() { // 新增attributionTag参数 private fun logPrivateDataAccess(opCode: String, attributionTag: String, trace: String) { Log.i("youzi", "Private data accessed. Operation: $opCode\n Attribution Tag:$attributionTag\n Stack Trace:\n $trace") } override fun onNoted(syncNotedAppOp: SyncNotedAppOp) { syncNotedAppOp.attributionTag?.let { logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString()) } } override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) { syncNotedAppOp.attributionTag?.let { logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString()) } } override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) { asyncNotedAppOp.attributionTag?.let { logPrivateDataAccess(syncNotedAppOp.op, it, Throwable().stackTrace.toString()) } } } // 创立AppOpsManager实例并增加下面定义的回调 val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback) }
咱们通过为刚刚的logPrivateDataAccess
新增了一个须要传的参数attributionTag
,而后在重写AppOpsManager.OnOpNotedCallback()
中的三个办法时,判断一下 attributionTag 是否为 null ,如果为 null 的话则示意与咱们须要检测的权限无关联,此时就不须要打印。
如果在利用中给多种隐衷权限设置了Tag,则能够在回调中拿到attributionTag的时候,判断一下是否是本人想要检测的那个Tag~
这个例子是写在 Activity 中的,如果须要全局应用的话,能够本人定义一个 LocationManager 的单例,而后对立设置 Tag,以便当前须要检测的时候能够应用归因标记
形式。
总结
顺便总结一下,在应用上面这段代码的时候,居然不晓得wifiManager.connectionInfo
这个办法会调用定位,当初“粗浅”的记住了。
val wifiManager = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager val wifiInfo = wifiManager.connectionInfo