com.faker.android.gradle.FakerDexTransform Maven / Gradle / Ivy
The newest version!
package com.faker.android.gradle;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.api.transform.Format;
import com.android.build.api.transform.QualifiedContent;
import com.android.build.api.transform.Status;
import com.android.build.api.transform.Transform;
import com.android.build.api.transform.TransformException;
import com.android.build.api.transform.TransformInput;
import com.android.build.api.transform.TransformInvocation;
import com.android.build.api.transform.TransformOutputProvider;
import com.android.build.gradle.internal.LoggerWrapper;
import com.android.build.gradle.internal.crash.PluginCrashReporter;
import com.android.build.gradle.internal.pipeline.ExtendedContentType;
import com.android.build.gradle.internal.pipeline.TransformManager;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.transforms.DexMergerTransform;
import com.android.build.gradle.internal.transforms.DexMergerTransformCallable;
import com.android.build.gradle.internal.transforms.TransformInputUtil;
import com.android.builder.dexing.DexArchiveEntry;
import com.android.builder.dexing.DexMergerTool;
import com.android.builder.dexing.DexingType;
import com.android.builder.dexing.FakerDexTools;
import com.android.dex.ClassDef;
import com.android.dex.Dex;
import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.MessageReceiver;
import com.android.ide.common.blame.ParsingProcessOutputHandler;
import com.android.ide.common.blame.parser.DexParser;
import com.android.ide.common.blame.parser.ToolOutputParser;
import com.android.ide.common.process.ProcessException;
import com.android.ide.common.process.ProcessOutput;
import com.android.ide.common.process.ProcessOutputHandler;
import com.faker.android.paker.Packer;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.TimeUnit;
import static com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_ASSETS;
public class FakerDexTransform extends Transform { //TODO 参数提取
private final boolean includeFeaturesInScopes;
private File assetCollosapeDex;
private VariantScope variantScope;
@NonNull
private final File collapseDexArchiveDir;
private final File collapseDexDir;
private Project project;
Set sensitiveStrings;
public FakerDexTransform(
Project project,
VariantScope variantScope,
boolean includeFeaturesInScopes,
DexingType dexingType,
int minSdkVersion,
boolean isDebuggable,
MessageReceiver messageReceiver,
DexMergerTool dexMerger,
String dexName,
Set sensitiveStrings
){
this.includeFeaturesInScopes = includeFeaturesInScopes;
this.variantScope = variantScope;
this.dexingType= dexingType;
this.minSdkVersion = minSdkVersion;
this.isDebuggable = isDebuggable;
this.messageReceiver = messageReceiver;
this.dexMerger = dexMerger;
assetCollosapeDex = new File(variantScope.getArtifacts().getFinalArtifactFiles(MERGED_ASSETS).getFiles().iterator().next(),dexName);
this.collapseDexArchiveDir = variantScope.getIncrementalDir(
variantScope.getFullVariantName() + "-" + "collosapeDexArchive");
this.collapseDexDir = variantScope.getIncrementalDir(
variantScope.getFullVariantName() + "-" + "collosapeDexDir");
this.sensitiveStrings = sensitiveStrings;
}
@Override
public Map getParameterInputs() {
Map params = Maps.newHashMapWithExpectedSize(6);
params.put("assetDex", assetCollosapeDex);
params.put("sensitiveStrings", sensitiveStrings);
return params;
}
@Override
public String getName() {
return "FakerDexMerger";
}
@Override
public Set getInputTypes() {
return ImmutableSet.of(ExtendedContentType.DEX_ARCHIVE);
}
@NonNull
@Override
public Set getOutputTypes() {
return ImmutableSet.of(ExtendedContentType.DEX_ARCHIVE);
}
@NonNull
@Override
public Set super QualifiedContent.Scope> getScopes() {
if (includeFeaturesInScopes) {
return TransformManager.SCOPE_FULL_WITH_IR_AND_FEATURES;
}
return TransformManager.SCOPE_FULL_WITH_IR_FOR_DEXING;
}
@Override
public boolean isIncremental() {
return true;
}
@NonNull
private File getDexOutputLocation(
@NonNull TransformOutputProvider outputProvider,
@NonNull String name,
@NonNull Set super QualifiedContent.Scope> scopes) {
return outputProvider.getContentLocation(name, getOutputTypes(), scopes, Format.DIRECTORY);
}
@Override
public void transform(TransformInvocation transformInvocation) throws TransformException, IOException, InterruptedException {
System.out.println("by passing dexArchives...");
// super.transform(transformInvocation);
transformInvocation.getOutputProvider().deleteAll();
//TODO my work
TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();
FileUtils.deleteDirectory(collapseDexArchiveDir);
// System.err.println("out path :"+target.getAbsolutePath());
Collection inputs = transformInvocation.getInputs();
//dirInputs
Iterator dirInputs = TransformInputUtil.getDirectories(inputs).stream().map(File::toPath).iterator();
while (dirInputs.hasNext()){
Path path = dirInputs.next();
// System.err.println("path :"+path);
List dexArchiveEntries = FakerDexTools.getEntriesFromSingleArchive(path);
File target = getDexOutputLocation(outputProvider,path.toFile().getAbsolutePath(),getScopes());
bypassDirInputs(path,dexArchiveEntries,sensitiveStrings,getRiseuniquePath(collapseDexArchiveDir),target);
}
//jarInputs
Iterator jarInputs = inputs.stream()
.flatMap(transformInput -> transformInput.getJarInputs().stream())
.filter(jarInput -> jarInput.getStatus() != Status.REMOVED)
.map(jarInput -> jarInput.getFile().toPath())
.iterator();
while (jarInputs.hasNext()){
Path path = jarInputs.next();
List dexArchiveEntries = FakerDexTools.getEntriesFromSingleArchive(path);
File target = getDexOutputLocation(outputProvider,path.toFile().getAbsolutePath(),getScopes());
bypassJarInputs(path,dexArchiveEntries,sensitiveStrings,getRiseuniquePath(collapseDexArchiveDir),target);
}
LoggerWrapper logger = LoggerWrapper.getLogger(DexMergerTransform.class);
ProcessOutputHandler outputHandler =
new ParsingProcessOutputHandler(
new ToolOutputParser(new DexParser(), Message.Kind.ERROR, logger),
new ToolOutputParser(new DexParser(), logger),
messageReceiver);
//TODO
File file[] = collapseDexArchiveDir.listFiles();
if(file==null||file.length==0){
return;
}
Iterator collosapeDexDexArchives = Arrays.stream(file).map(File::toPath).iterator();
ProcessOutput output = null;
if(!collapseDexDir.exists()){
collapseDexDir.mkdirs();
}
try (Closeable ignored = output = outputHandler.createOutput()){
List> mergeTasks = mergeDex(collosapeDexDexArchives,output,collapseDexDir);
mergeTasks.forEach(ForkJoinTask::join);
} catch (Exception e) {
PluginCrashReporter.maybeReportException(e);
logger.error(null, Throwables.getStackTraceAsString(e));
throw new TransformException(e);
} finally {
if (output != null) {
try {
outputHandler.handleOutput(output);
} catch (ProcessException e) {
// ignore this one
}
}
forkJoinPool.shutdown();
forkJoinPool.awaitTermination(100, TimeUnit.SECONDS);
}
//paker Dex
if(assetCollosapeDex.exists()){
assetCollosapeDex.delete();
}
Packer packer = new Packer(assetCollosapeDex);
Collection dexes = FileUtils.listFiles(collapseDexDir,new String[]{"dex"},false);
for (File dex :dexes){
System.out.println("collosape dex :"+dex.getAbsolutePath());
packer.addFile(dex.getName(),dex);
}
packer.saveToPath();
}
MessageReceiver messageReceiver;
DexingType dexingType;
DexMergerTool dexMerger;
private final ForkJoinPool forkJoinPool = new ForkJoinPool();
int minSdkVersion;
boolean isDebuggable;
@NonNull
private ForkJoinTask submitForMerging(
@NonNull ProcessOutput output,
@NonNull File dexOutputDir,
@NonNull Iterator dexArchives,
@Nullable Path mainDexList) {
DexMergerTransformCallable callable =
new DexMergerTransformCallable(
messageReceiver,
dexingType,
output,
dexOutputDir,
dexArchives,
mainDexList,
forkJoinPool,
dexMerger,
minSdkVersion,
isDebuggable);
return forkJoinPool.submit(callable);
}
private List> mergeDex(Iterator dexArchives,ProcessOutput output,File outputDir)
throws IOException {
if (!dexArchives.hasNext()) {
return ImmutableList.of();
}
return ImmutableList.of(submitForMerging(output, outputDir, dexArchives, null));
}
private void bypassDirInputs(Path path, List dexArchiveEntries, Set sensitiveStrings,File cache,File trans) {
for (DexArchiveEntry entry:dexArchiveEntries) {//
String relativePathInArchive = entry.getRelativePathInArchive();
try {
Dex dex = new Dex(entry.getDexFileContent());
File oDexArchive = new File(path.toFile(),relativePathInArchive);
if(isSensitiveDexArchive(dex,sensitiveStrings)){
// System.err.println("dir sensitive :"+relativePathInArchive);
File tDexArchive = new File(cache,relativePathInArchive);
FileUtils.copyFile(oDexArchive,tDexArchive);
continue;
}else {
File tDexArchive = new File(trans,relativePathInArchive);
FileUtils.copyFile(oDexArchive,tDexArchive);
}
// System.err.println("dir no sensitive :"+relativePathInArchive);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void bypassJarInputs(Path path, List dexArchiveEntries, Set sensitiveStrings, File intermediateDir,File target) {
File oDexArchive = path.toFile();
for (DexArchiveEntry entry:dexArchiveEntries) {//
// System.err.println("---- :"+entry.getRelativePathInArchive());
try {
Dex dex = new Dex(entry.getDexFileContent());
if(isSensitiveDexArchive(dex,sensitiveStrings)){
// System.err.println("jar sensitive :"+path); // && path
FileUtils.copyFile(oDexArchive,new File(intermediateDir.getParent(),intermediateDir.getName()+".jar"));
return;
}
} catch (IOException e) {
e.printStackTrace();
}
}
File tDexArchive = new File(target.getParent(),target.getName()+".jar");
try {
FileUtils.copyFile(oDexArchive,tDexArchive);
} catch (IOException e) {
e.printStackTrace();
}
}
private File getRiseuniquePath(File root){
if(!root.exists()){
root.mkdirs();
}
File current[] = root.listFiles();
if(current==null){
return new File(root,"0");
}
return new File(root,current.length+"");
}
private boolean isSensitiveDexArchive (Dex dex,Set sensitiveStrings) {
List strings = dex.typeNames();
Iterator classDefs = dex.classDefs().iterator();
while (classDefs.hasNext()){
ClassDef classDef = classDefs.next();
int typeIndex = classDef.getTypeIndex();
String className = strings.get(typeIndex);
for (String sensitiveString:sensitiveStrings){
if(className.contains(sensitiveString)){
return true;
}
}
}
return false;
}
@Override
public Collection getSecondaryFileOutputs() {//TODO 目标文件
return ImmutableList.of(assetCollosapeDex);
}
@Override
public Collection getSecondaryDirectoryOutputs() {//TODO 缓存目录
return ImmutableList.of(collapseDexArchiveDir);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy