一次Android權限刪除經歷

**html

1.事發通過

** 近期google play發佈了新的政策,其中一部分是限制權限使用,只容許知足條件的使用場景才能申請權限,小編所在的項目被檢測出使用了RECEIVE_SMS權限,可是從app下的Androidmanifest文件中並未發現有該權限的註冊,因此該權限是哪裏來的呢?java

2.初步定位

首先使用android studio查看了打包出來的apk中的Androidmanifest文件,發現其中確實存在RECEIVE_SMS權限,也就是說打包到apk中的Androidmanifest文件並非app下的該文件,從android開發者官網中合併多個manifest文件的文檔來看,實際上打包到apk中的manifest文件是由多個menifest文件合併而來的,其合併順序以下: node

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倉庫,能夠找到其實現核心以下: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文件的內容編程

4.修復方案

瞭解了其實現原理以後,開始整理其修復方案,主要須要解決的是,在合適的時間點去移除權限,也就是須要在其修改完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

5.覆盤

解決該問題主要涉及到 Androidmanifest.xml的合併,gradle構建生命週期,android打包流程和相關的gradle知識,當前對gradle的瞭解不夠,致使閱讀和理解比較耗時,接下來須要多關注,此外還有一個問題沒解決,就是採用.processManifest.finalizedBy這種方式時,發現androidmanifest文件經歷了以下狀況: 沒有注入權限->注入權限->刪除權限->又注入了權限 不知道是在哪一步又被注入了權限,仍是其餘狀況?併發

相關文章
相關標籤/搜索