Android国内应用的隐私政策问题,WebView加载Html界面获取设备信息问题的解决方案
Posted 林慈桥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android国内应用的隐私政策问题,WebView加载Html界面获取设备信息问题的解决方案相关的知识,希望对你有一定的参考价值。
背景
- 由于国家政策原因,需要应用或者游戏,提供隐私协议弹窗,并且在用户同意隐私协议之前,不得获取设备信息,比如MAC,androidID、SSID,WIFI信息等,当你的应用上架各大渠道的时候都需要去做这个功能,越往后面这个政策的执行力度会越大,刚开始的时候,随便做一个隐私弹框弹出来,渠道那边就能糊弄过去,后来渠道那边下通知告知隐私协议不合规,要整改,否则就下架处理,具体的整改内容就太多了,很多大渠道都提供了检测机制,先是机审后是人工审核。
隐私弹窗和协议界面实现
- 相信大多数应用或者游戏在实现隐私协议的时候都使用了WebView加载网页的方式,因为这个能尽可能做到动态加载不同的隐私协议地址,而且多个应用可以复用,前端可以动态去获取不同应用需要的协议,并展示给用户
WebView方式被检测到获取设备信息
- 突然有一天我们的应用被通报说有违规收集用户信息,后面把包拿去三方平台检测,居然没通过,说明如下:APP未见向用户明示SDK的收集使用规则,未经用户同意,SDK存在每30s读取一次位置信息等信息,非服务所必需且无合理应用场景,超出实现产品或服务的业务功能所必需的最低频率;刚开始有点懵,我们的应用肯定是用同意后才会去获取设备信息啊,咋个突然报了个这个,最后查到的罪魁祸首就是这个WebView了,当我们去加载隐私协议的时候它回去获取WIFI、位置等信息
- chromium SDK每2秒获取Wifi信息的堆栈信息如下
"stackTrace": ["className": "libcore.util.Janus", "level": 0, "fileName": "Janus.java", "methodName": "getData", "lineNumber": 831, "className": "android.net.wifi.WifiManager", "level": 0, "fileName": "WifiManager.java", "methodName": "getConnectionInfo", "lineNumber": 1676, "className": "org.chromium.net.NetworkChangeNotifierAutoDetect$WifiManagerDelegate", "level": 0, "fileName": "NetworkChangeNotifierAutoDetect.java", "methodName": "getWifiInfoLocked", "lineNumber": 28, "className": "org.chromium.net.NetworkChangeNotifierAutoDetect$WifiManagerDelegate", "level": 0, "fileName": "NetworkChangeNotifierAutoDetect.java", "methodName": "getWifiSsid", "lineNumber": 22, "className": "org.chromium.net.NetworkChangeNotifierAutoDetect", "level": 0, "fileName": "NetworkChangeNotifierAutoDetect.java", "methodName": "getCurrentNetworkState", "lineNumber": 67, "className": "org.chromium.net.NetworkChangeNotifierAutoDetect", "level": 0, "fileName": "NetworkChangeNotifierAutoDetect.java", "methodName": "<init>", "lineNumber": 21, "className": "org.chromium.content.browser.BackgroundSyncNetworkObserver", "level": 0, "fileName": "BackgroundSyncNetworkObserver.java", "methodName": "createObserver", "lineNumber": 15, "className": "org.chromium.base.SystemMessageHandler", "level": 0, "fileName": "SystemMessageHandler.java", "methodName": "nativeDoRunLoopOnce", "lineNumber": -2, "className": "org.chromium.base.SystemMessageHandler", "level": 0, "fileName": "SystemMessageHandler.java", "methodName": "handleMessage", "lineNumber": 9, "className": "android.os.Handler", "level": 0, "fileName": "Handler.java", "methodName": "dispatchMessage", "lineNumber": 106, "className": "android.os.Looper", "level": 0, "fileName": "Looper.java", "methodName": "loop", "lineNumber": 193, "className": "android.app.ActivityThread", "level": 0, "fileName": "ActivityThread.java", "methodName": "main", "lineNumber": 6754, "className": "java.lang.reflect.Method", "level": 0, "fileName": "Method.java", "methodName": "invoke", "lineNumber": -2, "className": "com.android.internal.os.RuntimeInit$MethodAndArgsCaller", "level": 0, "fileName": "RuntimeInit.java", "methodName": "run", "lineNumber": 506, "className": "com.android.internal.os.ZygoteInit", "level": 0, "fileName": "ZygoteInit.java", "methodName": "main", "lineNumber": 863], "permisson_group": "", "permisson": "", "result": "WifiInfo": "android.net.wifi.WifiInfo@abe5a02", "stack_txt": 2, "permisson_level": ""
- chromium SDK每2秒获取ssid的堆栈信息如下
"stackTrace": ["className": "libcore.util.Janus", "level": 0, "fileName": "Janus.java", "methodName": "getData", "lineNumber": 831, "className": "android.net.wifi.WifiInfo", "level": 0, "fileName": "WifiInfo.java", "methodName": "getSSID", "lineNumber": 272, "className": "org.chromium.net.NetworkChangeNotifierAutoDetect$WifiManagerDelegate", "level": 0, "fileName": "NetworkChangeNotifierAutoDetect.java", "methodName": "getWifiSsid", "lineNumber": 24, "className": "org.chromium.net.NetworkChangeNotifierAutoDetect", "level": 0, "fileName": "NetworkChangeNotifierAutoDetect.java", "methodName": "getCurrentNetworkState", "lineNumber": 67, "className": "org.chromium.net.NetworkChangeNotifierAutoDetect", "level": 0, "fileName": "NetworkChangeNotifierAutoDetect.java", "methodName": "connectionTypeChanged", "lineNumber": 100, "className": "org.chromium.android_webview.AwNetworkChangeNotifierRegistrationPolicy", "level": 0, "fileName": "AwNetworkChangeNotifierRegistrationPolicy.java", "methodName": "onFirstWebViewCreated", "lineNumber": 13, "className": "org.chromium.android_webview.AwContentsLifecycleNotifier", "level": 0, "fileName": "AwContentsLifecycleNotifier.java", "methodName": "onWebViewCreated", "lineNumber": 5, "className": "org.chromium.android_webview.AwContents", "level": 0, "fileName": "AwContents.java", "methodName": "nativeInit", "lineNumber": -2, "className": "org.chromium.android_webview.AwContents", "level": 0, "fileName": "AwContents.java", "methodName": "<init>", "lineNumber": 80, "className": "com.android.webview.chromium.WebViewChromium$1", "level": 0, "fileName": "WebViewChromium.java", "methodName": "run", "lineNumber": 14, "className": "org.chromium.android_webview.WebViewChromiumRunQueue", "level": 0, "fileName": "WebViewChromiumRunQueue.java", "methodName": "drainQueue", "lineNumber": 13, "className": "org.chromium.android_webview.WebViewChromiumRunQueue$1", "level": 0, "fileName": "WebViewChromiumRunQueue.java", "methodName": "run", "lineNumber": 2, "className": "org.chromium.base.ThreadUtils", "level": 0, "fileName": "ThreadUtils.java", "methodName": "runOnUiThread", "lineNumber": 30, "className": "org.chromium.android_webview.WebViewChromiumRunQueue", "level": 0, "fileName": "WebViewChromiumRunQueue.java", "methodName": "addTask", "lineNumber": 7, "className": "com.android.webview.chromium.WebViewChromiumFactoryProvider", "level": 0, "fileName": "WebViewChromiumFactoryProvider.java", "methodName": "addTask", "lineNumber": 6, "className": "com.android.webview.chromium.WebViewChromium", "level": 0, "fileName": "WebViewChromium.java", "methodName": "init", "lineNumber": 88, "className": "android.webkit.WebView", "level": 0, "fileName": "WebView.java", "methodName": "<init>", "lineNumber": 427, "className": "android.webkit.WebView", "level": 0, "fileName": "WebView.java", "methodName": "<init>", "lineNumber": 353, "className": "android.webkit.WebView", "level": 0, "fileName": "WebView.java", "methodName": "<init>", "lineNumber": 336, "className": "android.webkit.WebView", "level": 0, "fileName": "WebView.java", "methodName": "<init>", "lineNumber": 323, "className": "java.lang.reflect.Constructor", "level": 0, "fileName": "Constructor.java", "methodName": "newInstance0", "lineNumber": -2, "className": "java.lang.reflect.Constructor", "level": 0, "fileName": "Constructor.java", "methodName": "newInstance", "lineNumber": 343, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "createView", "lineNumber": 647, "className": "com.android.internal.policy.PhoneLayoutInflater", "level": 0, "fileName": "PhoneLayoutInflater.java", "methodName": "onCreateView", "lineNumber": 58, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "onCreateView", "lineNumber": 720, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "createViewFromTag", "lineNumber": 788, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "createViewFromTag", "lineNumber": 730, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "rInflate", "lineNumber": 863, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "rInflateChildren", "lineNumber": 824, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "inflate", "lineNumber": 515, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "inflate", "lineNumber": 423, "className": "android.view.LayoutInflater", "level": 0, "fileName": "LayoutInflater.java", "methodName": "inflate", "lineNumber": 374, "className": "com.android.internal.policy.PhoneWindow", "level": 0, "fileName": "PhoneWindow.java", "methodName": "setContentView", "lineNumber": 436, "className": "android.app.Dialog", "level": 0, "fileName": "Dialog.java", "methodName": "setContentView", "lineNumber": 557, "className": "com.archly.asdk.privacy.PrivacyDialog", "level": 0, "fileName": "PrivacyDialog.java", "methodName": "onCreate", "lineNumber": 45, "className": "android.app.Dialog", "level": 0, "fileName": "Dialog.java", "methodName": "dispatchOnCreate", "lineNumber": 407, "className": "android.app.Dialog", "level": 0, "fileName": "Dialog.java", "methodName": "show", "lineNumber": 302, "className": "com.archly.asdk.privacy.PrivacyDialog", "level": 0, "fileName": "PrivacyDialog.java", "methodName": "show", "lineNumber": 110, "className": "com.archly.asdk.privacy.PrivacyHelper$1", "level": 0, "fileName": "PrivacyHelper.java", "methodName": "onCall", "lineNumber": 45, "className": "com.archly.asdk.privacy.PrivacyHelper$3$1", "level": 0, "fileName": "PrivacyHelper.java", "methodName": "run", "lineNumber": 96, "className": "android.os.Handler", "level": 0, "fileName": "Handler.java", "methodName": "handleCallback", "lineNumber": 873, "className": "android.os.Handler", "level": 0, "fileName": "Handler.java", "methodName": "dispatchMessage", "lineNumber": 99, "className": "android.os.Looper", "level": 0, "fileName": "Looper.java", "methodName": "loop", "lineNumber": 193, "className": "android.app.ActivityThread", "level": 0, "fileName": "ActivityThread.java", "methodName": "main", "lineNumber": 6754, "className": "java.lang.reflect.Method", "level": 0, "fileName": "Method.java", "methodName": "invoke", "lineNumber": -2, "className": "com.android.internal.os.RuntimeInit$MethodAndArgsCaller", "level": 0, "fileName": "RuntimeInit.java", "methodName": "run", "lineNumber": 506, "className": "com.android.internal.os.ZygoteInit", "level": 0, "fileName": "ZygoteInit.java", "methodName": "main", "lineNumber": 863], "permisson_group": "", "permisson": "android.permission.ACCESS_WIFI_STATE", "result": "String": "<unknown ssid>", "stack_txt": 2, "permisson_level": "normal"
如何处理
- 网上有很多关于WebWiew实现隐私政策功能的解决方案,比如采用hook(Android WebView违规获取MAC地址解决方案 - 代码先锋网)的方式,这个方式我们试过了,不得行
- 使用第三方开源框架去加载远端html文件,我们为了加载这个东西引入一个库,是在是没必要,最后也尝试了,不得行
- 后面我们直接考虑使用TextView加载富文本的方式进行处理,服务器的后端还是需要开发html界面,只不过这个界面的元素很少,而且是TextView能够识别的一些元素,服务器端提供一个api接口将htlm的内容返回给android前端,然后前端自己通过TextView去渲染富文本,只要弹窗里面固定几个隐私协议点击跳转到隐私界面就可以了,具体的html内容就不要加任何链接的东西了,TextView也不支持,一般隐私弹窗都是放到启动页的,启动页以及Application里面只要不去调用任何第三方SDK有获取设备信息的代码,就能保证在同意之前不去获取设备信息
重新检测终于通了
- 通过TextView的方式加载html出的包在第三方平台去检测,终于通过了,也证实了WebView加载html文件会去获取设备信息
- 如果您有其他的更好解决方案,欢迎提供哦
以上是关于Android国内应用的隐私政策问题,WebView加载Html界面获取设备信息问题的解决方案的主要内容,如果未能解决你的问题,请参考以下文章
永远不合规的Android应用隐私政策-Andrid开发者的吐槽
在 Playstore 上上传应用程序,但响应是需要隐私政策的权限:(android.permission.CAMERA)