前言
看了一圈各大网站目前对于隐衷合规检测的分享,发现大家简直都是本人写一套动静代理、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 办法是本人写的,名字可随便定义,用于打印应用到的权限名字,还有应用到的代码堆栈,不便大家看到在哪里调用了某个权限。
这里给个笔者检测时打印进去的日志示例:
![
](https://upload-images.jianshu…)
能够看到,打印出的日志当中会有应用到的定位权限名字 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