**html
** 近期google play发布了新的政策,其中一部分是限制权限使用,只容许知足条件的使用场景才能申请权限,小编所在的项目被检测出使用了RECEIVE_SMS权限,可是从app下的Androidmanifest文件中并未发现有该权限的注册,因此该权限是哪里来的呢?java
首先使用android studio查看了打包出来的apk中的Androidmanifest文件,发现其中确实存在RECEIVE_SMS权限,也就是说打包到apk中的Androidmanifest文件并非app下的该文件,从android开发者官网中合并多个manifest文件的文档来看,实际上打包到apk中的manifest文件是由多个menifest文件合并而来的,其合并顺序以下: node
到此已经找到了问题的制造者,接下来就是看下他是怎么实现的,面向google编程,搜索com.mob.sdk source code 找到maven仓库,能够找到其实现核心以下:android
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文件的内容编程
了解了其实现原理以后,开始整理其修复方案,主要须要解决的是,在合适的时间点去移除权限,也就是须要在其修改完Androidmanifest文件以后,和Androidmanifest文件被打包到apk中以前这段时间,这里涉及到gradle打包中的各个函数调用顺序,详细的打包流程参考这里,详细的任务在这里,我这里选择的切入点在processResources的任务执行以前,详细代码以下:api
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权限相关。固然,前提是项目中确实没有使用该权限,因此移除不会致使相关问题。bash
解决该问题主要涉及到 Androidmanifest.xml的合并,gradle构建生命周期,android打包流程和相关的gradle知识,当前对gradle的了解不够,致使阅读和理解比较耗时,接下来须要多关注,此外还有一个问题没解决,就是采用.processManifest.finalizedBy这种方式时,发现androidmanifest文件经历了以下状况: 没有注入权限->注入权限->删除权限->又注入了权限 不知道是在哪一步又被注入了权限,仍是其余状况?并发