一次Android权限删除经历

1.事发经过

近期google play发布了新的政策,其中一部分是限制权限使用,只允许满足条件的使用场景才能申请权限,小编所在的项目被检测出使用了RECEIVE_SMS权限,但是从app下的Androidmanifest文件中并未发现有该权限的注册,所以该权限是哪里来的呢?

2.初步定位

首先使用android studio查看了打包出来的apk中的Androidmanifest文件,发现其中确实存在RECEIVE_SMS权限,也就是说打包到apk中的Androidmanifest文件并不是app下的该文件,从android开发者官网中合并多个manifest文件的文档来看,实际上打包到apk中的manifest文件是由多个menifest文件合并而来的,其合并顺序如下:

manifest文件合并规则

优先级由低到高分别是:
第三方库中 < app模块 < app模块的源集
当合并发生冲突的时候,可以使用合并规则标记来处理冲突,所以我这里使用了tools:node="remove"来处理移除RECEIVE_SMS权限,重新打包查看结果发现,仍然存在该权限,也就是说该标记失效了吗?再思考一下,是否有记录合并过程的文件呢?答案是有的,如下:
合并规则结束生成的android manifest文件:app/build/intermediates/manifests/full/googleplay/debug/AndroidManifest.xml
合并操作记录文件:
app/build/outputs/logs/manifest-merger-googleplay-debug-report.txt
上述的googleplay是自定义的productFlavors,如果未定义就是app。
查看AndroidManifest.xml发现确实存在RECEIVE_SMS权限,但是查看manifest-merger-googleplay-debug-report.txt却找不到该权限的合并记录;也就是说,在正常的合并流程中,并没有RECEIVE_SMS权限的写入,会不会有人破坏了正常的合并流程呢?没办法,需要追踪到具体是哪一个第三方库引入了该权限,仔细对比了下apk中的Androidmenifest文件和app下的该文件,发现每次都是在该文件末尾多出了RECEIVE_SMS和其他一些东西,仔细一看发现是mobsdk相关的,于是剔除了该库,再编译发现还是存在该权限。。仔细想下,有点不对,因为要破坏manifest文件的合并,那么普通的第三方库是不行的,至少需要第三方gradle插件,于是移除了“com.mob.sdk”插件,再打包发现确实没有RECEIVE_SMS了

3.原理解析

到此已经找到了问题的制造者,接下来就是看下他是怎么实现的,面向google编程,搜索com.mob.sdk source code 找到maven仓库,可以找到其实现核心如下:

project.afterEvaluate {
            def android = project.extensions.getByName("android")
            if (globalVariants.autoConfig == null) {
                globalVariants.autoConfig = true
            }
            if (globalVariants.autoConfig) {
                if (android != null) {
                    configShareSDKXML(android)

                    def variants = null
                    boolean appModel = false
                    try {
                        variants = android.applicationVariants
                        appModel = true
                    } catch (Throwable t) {
                        try {
                            variants = android.libraryVariants
                        } catch (Throwable tt) {}
                    }
                    if (variants != null) {
                        variants.all { variant ->
                            variant.outputs.each { output ->
                                output.processManifest.doLast {
                                    configManifest(output, appModel, variant)
                                }
                            }
                        }
                    }
                }
            }
        }
private void configManifest(def output, boolean appModel, def variant) {
            ...
        manifestFiles.add(new File(output.processManifest.manifestOutputDirectory, "AndroidManifest.xml"))
        manifestFiles.each { manifestFile->
            if (manifestFile != null && manifestFile.exists()) {
                ...
                shouldAdd.each { per ->
                    String lastPermission = "<uses-permission ${ns} android:name=\"${per}\" />"
                    if (packageName != null && lastPermission.contains('${applicationId}')) {
                        lastPermission = lastPermission.replace('${applicationId}', packageName)
                    }
                    def permission = parser.parseText(lastPermission)
                    manifest.appendNode(permission)
                }
                ...
                def nsCustom = 'xmlns:android="http://schemas.android.com/apk/res/android"'
                def level = 'android:protectionLevel="signature"'
                shouldAddCoustom.each { per ->
                    String lastPermission = "<permission ${nsCustom} android:name=\"${per}\" ${level}/>"
                    if (packageName != null && lastPermission.contains('${applicationId}')) {
                        lastPermission = lastPermission.replace('${applicationId}', packageName)
                    }
                    def permission = parser.parseText(lastPermission)

                    manifest.appendNode(permission)
                }

                ...

                manifestFile.setText(XmlUtil.serialize(manifest), "utf-8")
            }

可以看出其hook了gradle的解析了配置之后注入了gradle任务(任务相关可以参考官网),详细的gradle的构建周期函数可以参考这个文章。在processManifest任务执行之后执行了他自己的动作,也就是更改androidmanifest文件的内容

4.修复方案

了解了其实现原理之后,开始整理其修复方案,主要需要解决的是,在合适的时间点去移除权限,也就是需要在其修改完Androidmanifest文件之后,和Androidmanifest文件被打包到apk中之前这段时间,这里涉及到gradle打包中的各个函数调用顺序,详细的打包流程参考这里,详细的任务在这里,我这里选择的切入点在processResources的任务执行之前,详细代码如下:

project.afterEvaluate {
        project.android.applicationVariants.all { variant ->
            variant.outputs.each { output ->
                output.processResources.doFirst { pm->
                    String manifestPath = output.processResources.manifestFile;
                    def manifestContent = file(manifestPath).getText()
                    manifestContent = manifestContent.replace('<uses-permission android:name="android.permission.RECEIVE_SMS"/>', '')
                    file(manifestPath).write(manifestContent)
                }
            }
        }
    }

主要的处理就是剔除其中的RECEIVE_SMS权限相关。当然,前提是项目中确实没有使用该权限,所以移除不会导致相关问题。

5.复盘

解决该问题主要涉及到 Androidmanifest.xml的合并,gradle构建生命周期,android打包流程和相关的gradle知识,当前对gradle的了解不够,导致阅读和理解比较耗时,接下来需要多关注,此外还有一个问题没解决,就是采用.processManifest.finalizedBy这种方式时,发现androidmanifest文件经历了如下情况:
没有注入权限->注入权限->删除权限->又注入了权限
不知道是在哪一步又被注入了权限,还是其他情况?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容