写点什么

一种有效管控 APP 隐私权限的解决方案,Android400 道面试题通关宝典助你进大厂

用户头像
Android架构
关注
发布于: 刚刚

setInjectableValues(


manifestMergerInvoker,


packageOverride, versionCode, versionName,


minSdkVersion, targetSdkVersion, maxSdkVersion


)


//关注这里的调用


val mergingReport = manifestMergerInvoker.merge()


//省略其他对 merge 结果处理代码


... ...


return mergingReport


} catch (e: ManifestMerger2.MergeFailureException) {


// TODO: unacceptable.


throw RuntimeException(e)


}


}


接着看 manifestMergerInvoker.merge()的实现


package com.android.manifmerger;


/**


  • merges android manifest files, idempotent.


*/


@Immutable


public class ManifestMerger2 {


public static class Invoker<T extends Invoker<T>>{


@NonNull


public MergingReport merge() throws MergeFailureException {


// provide some free placeholders values.


ImmutableMap<ManifestSystemProperty, Object> systemProperties = mSystemProperties.build();


... ...


FileStreamProvider fileStreamProvider = mFileStreamProvider != null


? mFileStreamProvider : new FileStreamProvider();


ManifestMerger2 manifestMerger =


new ManifestMerger2(


mLogger,


mMainManifestFile,


mLibraryFilesBuilder.build(),


mFlavorsAndBuildTypeFiles.build(),


mFeaturesBuilder.build(),


mPlaceholders.build(),


new MapBasedKeyBasedValueResolver<ManifestSystemProperty>(


systemProperties),


mMergeType,


mDocumentType,


Optional.fromNullable(mReportFile),


mFeatureName,


fileStreamProvider,


mNavigationFilesBuilder.build());


//调用下面的 private MergingReport merge()方法


return manifestMerger.merge();


}


}


/**


  • Perform high level ordering of files merging and delegates actual merging to

  • {@link XmlDocument#merge(XmlDocument, com.android.manifmerger.MergingReport.Builder)}

  • @return the merging activity report.

  • @throws MergeFailureException if the merging cannot be completed (for instance, if xml

  • files cannot be loaded).


*/


@NonNull


private MergingReport merge() throws MergeFailureException {


// initiate a new merging report


MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);


//一系列 merge manifest 规则处理


... ...


MergingReport mergingReport = mergingReportBuilder.build();


if (mReportFile.isPresent()) {


writeReport(mergingReport);


}


return mergingReport;


}


//最终写入 Log 文件方法


/**


  • Creates the merging report file.

  • @param mergingReport the merging activities report to serialize.


*/


private void writeReport(@NonNull MergingReport mergingReport) {


FileWriter fileWriter = null;


... ...


fileWriter = new FileWriter(mReportFile.get());


mergingReport.getActions().log(fileWriter);


}


}


到目前为止,从代码层面看到了 Log 文件是如何生成的。


方案实现




【manifest-merger-${variantname}-report.txt】文件大致内容如下:


-- Merging decision tree log ---


manifest


ADDED from /somepath/AndroidManifest.xml:x:x-xx:xx


MERGED from [dependencies sdk] /somepath/AndroidManifest.xml:x:x-xx:xx


INJECTED from /somepath/AndroidManifest.xml:x:x-xx:xx


...


uses-permission#android.permission.INTERNET


方案代码实现很简单:


1.自定义一个 Extension,列出暂禁用的权限;


2.实现相应 Plugin 和 Task;


Extension 定义可以如下所示:


host{


//明确暂禁用的权限列表


forbiddenPermissions = ['android.permission.GET_ACCOUNTS',


'a


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ndroid.permission.SEND_SMS',


'android.permission.CALL_PHONE',


'android.permission.BLUETOOTH',


... ...]


}


Plugin 简单示例:


public class HostPlugin implements Plugin<Project> {


@Override


final void apply(Project project) {


if (!project.getPlugins().hasPlugin('com.android.application') && !project.getPlugins().hasPlugin('com.android.library')) {


throw new GradleException('apply plugin: 'com.android.application' or apply plugin: 'com.android.library' is required')


}


HostExtension hostExtension = project.getExtensions().create('host', HostExtension.class)


project.afterEvaluate {


def variants = null;


if (project.plugins.hasPlugin('com.android.application')) {


variants = android.getApplicationVariants()


} else if (project.plugins.hasPlugin('com.android.library')) {


variants = android.getLibraryVariants()


}


variants?.all { BaseVariant variant ->


MergeHostManifestTask taskConfiguration= new MergeHostManifestTask.CreationAction()


project.getTasks().create(taskConfiguration.getName(), taskConfiguration.getType(), taskConfiguration)


}


}


}


}


Task 简单示例:


import org.gradle.util.GFileUtils


import com.android.utils.FileUtils


class MergeHostManifestTask extends DefaultTask {


List<String> forbiddenPermissions //禁用的权限列表


VariantScope scope


@TaskAction


def doFullTaskAction() {


File logFile = FileUtils.join(


scope.getGlobalScope().getOutputsDir(),


"logs",


"manifest-permissions-validate-"


  • scope.getVariantConfiguration().getBaseName()

  • "-report.txt")


GFileUtils.mkdirs(logFile.getParentFile())


GFileUtils.deleteQuietly(logFile)


checkHostManifest(forbiddenPermissions,logFile,scope)


if (logFile.exists() && logFile.length() > 0) {


throw new GradleException("Has forbidden permissions in host, please check it in file ${logFile.getAbsolutePath()}")


}


}


/**


  • 检测 host manifest 是否含有禁用权限列表

  • @param forbiddenPermissions

  • @param logFile

  • @param variantScope


*/


public static void checkHostManifest(List<String> forbiddenPermissions, File logFile, def variantScope) {


if (forbiddenPermissions == null || forbiddenPermissions.isEmpty()) {


return


}


File reportFile =


FileUtils.join(


variantScope.getGlobalScope().getOutputsDir(),


"logs",


"manifest-merger-"


  • variantScope.getVariantConfiguration().getBaseName()

  • "-report.txt")


if (!reportFile.exists()) {


return


}


reportFile.withReader { reader ->


String line


while ((line = reader.readLine()) != null) {


forbiddenPermissions.each { p ->


if (line.contains("uses-permission#${p.trim()}")) {


logFile.append("${p.trim()}\n")


logFile.append(reader.readLine())


logFile.append("\n")


}


}


}


}


}


public static class CreationAction


extends TaskConfiguration<MergeHostManifestTask> {


BaseVariant variant


Project project


public CreationAction(Project project,BaseVariant variant){


this.project= project


this.variant=variant


}


@Override


void execute(MergeHostManifestTask task) {


... ...


HostExtension hostExtension = project.getExtensions().findByType(HostExtension.class)


task.forbiddenPermissions = hostExtension.getForbiddenPermissions()


task.scope= variant.getMetaClass().getProperty(variant, 'variantData').getScope()


task.dependsOn getProcessManifestTask()


}


private Task getProcessManifestTaskCompat() {


try {


//>=3.3.0


String taskName = variant.getMetaClass().getProperty(variant, 'variantData').getScope().getTaskContainer().getProcessManifestTask().getName()


return project.getTasks().findByName(taskName)


} catch (Exception e) {


}


}


}


如果 APP 或其依赖的 SDK,有引入禁用权限,则会抛出编译异常,生成的【manifest-permissions-validate-${variantname}-report.txt】文件内容类似以下所示:

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
一种有效管控APP隐私权限的解决方案,Android400道面试题通关宝典助你进大厂