package com.taobao.android;
import com.google.common.collect.Lists;
import com.taobao.android.apatch.utils.TypeGenUtil;
import com.taobao.android.differ.dex.DexDiffer;
import com.taobao.android.differ.dex.PatchException;
import com.taobao.android.filter.DexDiffFilter;
import com.taobao.android.object.ClassDiffInfo;
import com.taobao.android.object.DexDiffInfo;
import com.taobao.android.object.DiffType;
import com.taobao.android.smali.AfBakSmali;
import com.taobao.android.smali.SmaliMod;
import org.antlr.runtime.RecognitionException;
import org.apache.commons.io.FileUtils;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.dexbacked.DexBackedClassDef;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.DexFile;
import org.jf.dexlib2.util.SyntheticAccessorResolver;
import org.jf.dexlib2.writer.builder.DexBuilder;
import org.jf.dexlib2.writer.io.FileDataStore;
import org.jf.util.ClassFileNameHandler;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* 为动态部署的dex diff的工具类
* Created by shenghua.nish on 2016-02-19 上午11:15.
*/
public class TPatchDexTool {
private List<File> baseDexFiles;
private List<File> newDexFiles;
private File outDexFile;
private int apiLevel;
private DexDiffer dexDiffer;
private DexDiffFilter dexDiffFilter;
private boolean mainBundle;
private Map<String, ClassDef>lastBundleClassMap = new HashMap<String, ClassDef>();
private boolean removeDupStrings;
public TPatchDexTool(List<File> baseDexFiles, List<File> newDexFiles, int apiLevel, Map<String,ClassDef> map,boolean mainBundle) {
this.baseDexFiles = baseDexFiles;
this.newDexFiles = newDexFiles;
this.apiLevel = apiLevel;
this.mainBundle = mainBundle;
assert (null != baseDexFiles && baseDexFiles.size() > 0);
assert (null != newDexFiles && newDexFiles.size() > 0);
this.dexDiffer = new DexDiffer(baseDexFiles, newDexFiles, apiLevel);
if (map == null) {
dexDiffer.setLastBundleClassMap(lastBundleClassMap);
}else {
dexDiffer.setLastBundleClassMap(map);
}
}
public TPatchDexTool(File baseDex, File newDex, int apiLevel,boolean mainBundle) {
baseDexFiles = Lists.newArrayList();
baseDexFiles.add(baseDex);
newDexFiles = Lists.newArrayList();
newDexFiles.add(newDex);
this.mainBundle = mainBundle;
this.dexDiffer = new DexDiffer(baseDex, newDex, apiLevel);
dexDiffer.setLastBundleClassMap(lastBundleClassMap);
}
public void setDexDiffFilter(DexDiffFilter dexDiffFilter) {
this.dexDiffFilter = dexDiffFilter;
this.dexDiffer.setDexDiffFilter(dexDiffFilter);
}
/**
* 生成淘宝的动态部署的patch的Dex文件
*
* @param outDexFile
*/
public DexDiffInfo createTPatchDex(File outDexFile) throws IOException, RecognitionException, PatchException {
DexDiffInfo dexDiffInfo = null;
// if (mainBundle){
outDexFile.getParentFile().mkdirs();
dexDiffInfo = dexDiffer.doDiff();
// 将有变动的类写入到diff.dex
final Set<ClassDef> modifyClasses = new HashSet<ClassDef>();
for (ClassDiffInfo classDiffInfo : dexDiffInfo.getClassDiffInfoMap().values()) {
if (DiffType.MODIFY.equals(classDiffInfo.getType()) || DiffType.ADD.equals(classDiffInfo.getType()) || DiffType.OVERRIDE.equals(classDiffInfo.getType())) {
modifyClasses.add(classDiffInfo.getClassDef());
}
}
if (modifyClasses.size() > 0) {
DexFileFactory.writeDexFile(outDexFile.getAbsolutePath(), new DexFile() {
@Nonnull
@Override
public Set<? extends ClassDef> getClasses() {
return new AbstractSet<ClassDef>() {
@Nonnull
@Override
public Iterator<ClassDef> iterator() {
return modifyClasses.iterator();
}
@Override
public int size() {
return modifyClasses.size();
}
};
}
});
}
// }else {
// dexDiffInfo = dexDiffer.doDiff();
//// DexPatchGenerator dexPatchGenerator = new DexPatchGenerator(baseDexFiles.get(0),removeDebugInfo(newDexFiles.get(0)));
//// dexPatchGenerator.executeAndSaveTo(outDexFile);
// }
return dexDiffInfo;
}
public static baksmaliOptions getBuildOption(Iterable<? extends ClassDef> collection, int apiLevel) {
baksmaliOptions options = new baksmaliOptions();
options.deodex = false;
options.noParameterRegisters = false;
options.useLocalsDirective = true;
options.useSequentialLabels = true;
options.outputDebugInfo = false;
options.addCodeOffsets = false;
options.jobs = -1;
options.noAccessorComments = false;
options.registerInfo = 0;// 128
options.ignoreErrors = false;
options.inlineResolver = null;
options.apiLevel = apiLevel;
options.checkPackagePrivateAccess = false;
if (!options.noAccessorComments) {
options.syntheticAccessorResolver = new SyntheticAccessorResolver(collection);
}
return options;
}
public static File removeDebugInfo(File file) {
File smaliDir = null;
if (file == null || !file.exists() || !file.isFile()) {
return null;
}
try {
Set<? extends DexBackedClassDef> classes = DexFileFactory.loadDexFile(file, 19, true).getClasses();
DexBuilder dexBuilder = DexBuilder.makeDexBuilder();
smaliDir = new File(file.getParentFile(), "smali");
smaliDir.mkdirs();
final ClassFileNameHandler outFileNameHandler = new ClassFileNameHandler(smaliDir, ".smali");
final ClassFileNameHandler inFileNameHandler = new ClassFileNameHandler(smaliDir, ".smali");
for (DexBackedClassDef classDef : classes) {
AfBakSmali.disassembleClass(classDef, outFileNameHandler, getBuildOption(classes, 19), true, true);
String className = TypeGenUtil.newType(classDef.getType());
File smaliFile = inFileNameHandler.getUniqueFilenameForClass(className);
SmaliMod.assembleSmaliFile(smaliFile, dexBuilder, true, true);
}
dexBuilder.writeTo(new FileDataStore(file));
FileUtils.deleteDirectory(smaliDir);
} catch (Exception e) {
}
return file;
}
}