All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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 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