com.tencent.tinker.commons.dexpatcher.DexPatchApplier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tinker-commons Show documentation
Show all versions of tinker-commons Show documentation
Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstalling apk.
/*
* Tencent is pleased to support the open source community by making Tinker available.
*
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tencent.tinker.commons.dexpatcher;
import com.tencent.tinker.android.dex.Annotation;
import com.tencent.tinker.android.dex.AnnotationSet;
import com.tencent.tinker.android.dex.AnnotationSetRefList;
import com.tencent.tinker.android.dex.AnnotationsDirectory;
import com.tencent.tinker.android.dex.ClassData;
import com.tencent.tinker.android.dex.ClassDef;
import com.tencent.tinker.android.dex.Code;
import com.tencent.tinker.android.dex.DebugInfoItem;
import com.tencent.tinker.android.dex.Dex;
import com.tencent.tinker.android.dex.EncodedValue;
import com.tencent.tinker.android.dex.FieldId;
import com.tencent.tinker.android.dex.MethodId;
import com.tencent.tinker.android.dex.ProtoId;
import com.tencent.tinker.android.dex.StringData;
import com.tencent.tinker.android.dex.TableOfContents;
import com.tencent.tinker.android.dex.TypeList;
import com.tencent.tinker.android.dex.util.CompareUtils;
import com.tencent.tinker.android.dx.util.Hex;
import com.tencent.tinker.android.dx.util.IndexMap;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.AnnotationSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.AnnotationSetRefListSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.AnnotationSetSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.AnnotationsDirectorySectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.ClassDataSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.ClassDefSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.CodeSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.DebugInfoItemSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.DexSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.FieldIdSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.MethodIdSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.ProtoIdSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.StaticValueSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.StringDataSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.TypeIdSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.algorithms.patch.TypeListSectionPatchAlgorithm;
import com.tencent.tinker.commons.dexpatcher.struct.DexPatchFile;
import com.tencent.tinker.commons.dexpatcher.struct.SmallPatchedDexItemFile;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
/**
* Created by tangyinsheng on 2016/6/30.
*/
public class DexPatchApplier {
private final Dex oldDex;
private final Dex patchedDex;
/** May be null if we need to generate small patch. **/
private final DexPatchFile patchFile;
private final SmallPatchedDexItemFile extraInfoFile;
private final IndexMap oldToFullPatchedIndexMap;
private final IndexMap patchedToSmallPatchedIndexMap;
private final String oldDexSignStr;
private DexSectionPatchAlgorithm stringDataSectionPatchAlg;
private DexSectionPatchAlgorithm typeIdSectionPatchAlg;
private DexSectionPatchAlgorithm protoIdSectionPatchAlg;
private DexSectionPatchAlgorithm fieldIdSectionPatchAlg;
private DexSectionPatchAlgorithm methodIdSectionPatchAlg;
private DexSectionPatchAlgorithm classDefSectionPatchAlg;
private DexSectionPatchAlgorithm typeListSectionPatchAlg;
private DexSectionPatchAlgorithm annotationSetRefListSectionPatchAlg;
private DexSectionPatchAlgorithm annotationSetSectionPatchAlg;
private DexSectionPatchAlgorithm classDataSectionPatchAlg;
private DexSectionPatchAlgorithm codeSectionPatchAlg;
private DexSectionPatchAlgorithm debugInfoSectionPatchAlg;
private DexSectionPatchAlgorithm annotationSectionPatchAlg;
private DexSectionPatchAlgorithm encodedArraySectionPatchAlg;
private DexSectionPatchAlgorithm annotationsDirectorySectionPatchAlg;
public DexPatchApplier(File oldDexIn, File patchFileIn) throws IOException {
this(
new Dex(oldDexIn),
(patchFileIn != null ? new DexPatchFile(patchFileIn) : null),
null
);
}
public DexPatchApplier(InputStream oldDexIn, InputStream patchFileIn) throws IOException {
this(
new Dex(oldDexIn),
(patchFileIn != null ? new DexPatchFile(patchFileIn) : null),
null
);
}
public DexPatchApplier(InputStream oldDexIn, int initDexSize, InputStream patchFileIn) throws IOException {
this(
new Dex(oldDexIn, initDexSize),
(patchFileIn != null ? new DexPatchFile(patchFileIn) : null),
null
);
}
public DexPatchApplier(
File oldDexIn,
File patchFileIn,
SmallPatchedDexItemFile extraInfoFile
) throws IOException {
this(
new Dex(oldDexIn),
(patchFileIn != null ? new DexPatchFile(patchFileIn) : null),
extraInfoFile
);
}
public DexPatchApplier(
InputStream oldDexIn,
InputStream patchFileIn,
SmallPatchedDexItemFile extraInfoFile
) throws IOException {
this(
new Dex(oldDexIn),
(patchFileIn != null ? new DexPatchFile(patchFileIn) : null),
extraInfoFile
);
}
public DexPatchApplier(
InputStream oldDexIn,
int initDexSize,
InputStream patchFileIn,
SmallPatchedDexItemFile extraInfoFile
) throws IOException {
this(
new Dex(oldDexIn, initDexSize),
(patchFileIn != null ? new DexPatchFile(patchFileIn) : null),
extraInfoFile
);
}
public DexPatchApplier(
Dex oldDexIn,
DexPatchFile patchFileIn,
SmallPatchedDexItemFile extraAddedDexElementsFile
) {
this.oldDex = oldDexIn;
this.oldDexSignStr = Hex.toHexString(oldDexIn.computeSignature(false));
this.patchFile = patchFileIn;
if (extraAddedDexElementsFile == null) {
this.patchedDex = new Dex(patchFileIn.getPatchedDexSize());
} else {
this.patchedDex = new Dex(
extraAddedDexElementsFile.getPatchedDexSizeByOldDexSign(this.oldDexSignStr)
);
}
this.oldToFullPatchedIndexMap = new IndexMap();
this.patchedToSmallPatchedIndexMap = (extraAddedDexElementsFile != null ? new IndexMap() : null);
this.extraInfoFile = extraAddedDexElementsFile;
if ((patchFileIn == null) && (extraAddedDexElementsFile == null
|| !extraAddedDexElementsFile.isAffectedOldDex(this.oldDexSignStr))) {
throw new UnsupportedOperationException(
"patchFileIn is null, and extraAddedDexElementFile"
+ "(smallPatchInfo) is null or does not mention "
+ "oldDexIn."
);
}
}
public void executeAndSaveTo(OutputStream out) throws IOException {
// Before executing, we should check if this patch can be applied to
// old dex we passed in.
byte[] oldDexSign = this.oldDex.computeSignature(false);
if (oldDexSign == null) {
throw new IOException("failed to compute old dex's signature.");
}
if (this.patchFile != null) {
byte[] oldDexSignInPatchFile = this.patchFile.getOldDexSignature();
if (CompareUtils.uArrCompare(oldDexSign, oldDexSignInPatchFile) != 0) {
throw new IOException(
String.format(
"old dex signature mismatch! expected: %s, actual: %s",
Arrays.toString(oldDexSign),
Arrays.toString(oldDexSignInPatchFile)
)
);
}
}
String oldDexSignStr = Hex.toHexString(oldDexSign);
// Firstly, set sections' offset after patched, sort according to their offset so that
// the dex lib of aosp can calculate section size.
TableOfContents patchedToc = this.patchedDex.getTableOfContents();
patchedToc.header.off = 0;
patchedToc.header.size = 1;
patchedToc.mapList.size = 1;
if (extraInfoFile == null || !extraInfoFile.isAffectedOldDex(this.oldDexSignStr)) {
patchedToc.stringIds.off
= this.patchFile.getPatchedStringIdSectionOffset();
patchedToc.typeIds.off
= this.patchFile.getPatchedTypeIdSectionOffset();
patchedToc.typeLists.off
= this.patchFile.getPatchedTypeListSectionOffset();
patchedToc.protoIds.off
= this.patchFile.getPatchedProtoIdSectionOffset();
patchedToc.fieldIds.off
= this.patchFile.getPatchedFieldIdSectionOffset();
patchedToc.methodIds.off
= this.patchFile.getPatchedMethodIdSectionOffset();
patchedToc.classDefs.off
= this.patchFile.getPatchedClassDefSectionOffset();
patchedToc.mapList.off
= this.patchFile.getPatchedMapListSectionOffset();
patchedToc.stringDatas.off
= this.patchFile.getPatchedStringDataSectionOffset();
patchedToc.annotations.off
= this.patchFile.getPatchedAnnotationSectionOffset();
patchedToc.annotationSets.off
= this.patchFile.getPatchedAnnotationSetSectionOffset();
patchedToc.annotationSetRefLists.off
= this.patchFile.getPatchedAnnotationSetRefListSectionOffset();
patchedToc.annotationsDirectories.off
= this.patchFile.getPatchedAnnotationsDirectorySectionOffset();
patchedToc.encodedArrays.off
= this.patchFile.getPatchedEncodedArraySectionOffset();
patchedToc.debugInfos.off
= this.patchFile.getPatchedDebugInfoSectionOffset();
patchedToc.codes.off
= this.patchFile.getPatchedCodeSectionOffset();
patchedToc.classDatas.off
= this.patchFile.getPatchedClassDataSectionOffset();
patchedToc.fileSize
= this.patchFile.getPatchedDexSize();
} else {
patchedToc.stringIds.off
= this.extraInfoFile.getPatchedStringIdOffsetByOldDexSign(oldDexSignStr);
patchedToc.typeIds.off
= this.extraInfoFile.getPatchedTypeIdOffsetByOldDexSign(oldDexSignStr);
patchedToc.typeLists.off
= this.extraInfoFile.getPatchedTypeListOffsetByOldDexSign(oldDexSignStr);
patchedToc.protoIds.off
= this.extraInfoFile.getPatchedProtoIdOffsetByOldDexSign(oldDexSignStr);
patchedToc.fieldIds.off
= this.extraInfoFile.getPatchedFieldIdOffsetByOldDexSign(oldDexSignStr);
patchedToc.methodIds.off
= this.extraInfoFile.getPatchedMethodIdOffsetByOldDexSign(oldDexSignStr);
patchedToc.classDefs.off
= this.extraInfoFile.getPatchedClassDefOffsetByOldDexSign(oldDexSignStr);
patchedToc.mapList.off
= this.extraInfoFile.getPatchedMapListOffsetByOldDexSign(oldDexSignStr);
patchedToc.stringDatas.off
= this.extraInfoFile.getPatchedStringDataOffsetByOldDexSign(oldDexSignStr);
patchedToc.annotations.off
= this.extraInfoFile.getPatchedAnnotationOffsetByOldDexSign(oldDexSignStr);
patchedToc.annotationSets.off
= this.extraInfoFile.getPatchedAnnotationSetOffsetByOldDexSign(oldDexSignStr);
patchedToc.annotationSetRefLists.off
= this.extraInfoFile.getPatchedAnnotationSetRefListOffsetByOldDexSign(oldDexSignStr);
patchedToc.annotationsDirectories.off
= this.extraInfoFile.getPatchedAnnotationsDirectoryOffsetByOldDexSign(oldDexSignStr);
patchedToc.encodedArrays.off
= this.extraInfoFile.getPatchedEncodedArrayOffsetByOldDexSign(oldDexSignStr);
patchedToc.debugInfos.off
= this.extraInfoFile.getPatchedDebugInfoOffsetByOldDexSign(oldDexSignStr);
patchedToc.codes.off
= this.extraInfoFile.getPatchedCodeOffsetByOldDexSign(oldDexSignStr);
patchedToc.classDatas.off
= this.extraInfoFile.getPatchedClassDataOffsetByOldDexSign(oldDexSignStr);
patchedToc.fileSize
= this.extraInfoFile.getPatchedDexSizeByOldDexSign(oldDexSignStr);
}
Arrays.sort(patchedToc.sections);
patchedToc.computeSizesFromOffsets();
// Secondly, run patch algorithms according to sections' dependencies.
this.stringDataSectionPatchAlg = new StringDataSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.typeIdSectionPatchAlg = new TypeIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.protoIdSectionPatchAlg = new ProtoIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.fieldIdSectionPatchAlg = new FieldIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.methodIdSectionPatchAlg = new MethodIdSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.classDefSectionPatchAlg = new ClassDefSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.typeListSectionPatchAlg = new TypeListSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.annotationSetRefListSectionPatchAlg = new AnnotationSetRefListSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.annotationSetSectionPatchAlg = new AnnotationSetSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.classDataSectionPatchAlg = new ClassDataSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.codeSectionPatchAlg = new CodeSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.debugInfoSectionPatchAlg = new DebugInfoItemSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.annotationSectionPatchAlg = new AnnotationSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.encodedArraySectionPatchAlg = new StaticValueSectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.annotationsDirectorySectionPatchAlg = new AnnotationsDirectorySectionPatchAlgorithm(
patchFile, oldDex, patchedDex, oldToFullPatchedIndexMap,
patchedToSmallPatchedIndexMap, extraInfoFile
);
this.stringDataSectionPatchAlg.execute();
this.typeIdSectionPatchAlg.execute();
this.typeListSectionPatchAlg.execute();
this.protoIdSectionPatchAlg.execute();
this.fieldIdSectionPatchAlg.execute();
this.methodIdSectionPatchAlg.execute();
Runtime.getRuntime().gc();
this.annotationSectionPatchAlg.execute();
this.annotationSetSectionPatchAlg.execute();
this.annotationSetRefListSectionPatchAlg.execute();
this.annotationsDirectorySectionPatchAlg.execute();
Runtime.getRuntime().gc();
this.debugInfoSectionPatchAlg.execute();
this.codeSectionPatchAlg.execute();
Runtime.getRuntime().gc();
this.classDataSectionPatchAlg.execute();
this.encodedArraySectionPatchAlg.execute();
this.classDefSectionPatchAlg.execute();
Runtime.getRuntime().gc();
// Thirdly, write header, mapList. Calculate and write patched dex's sign and checksum.
Dex.Section headerOut = this.patchedDex.openSection(patchedToc.header.off);
patchedToc.writeHeader(headerOut);
Dex.Section mapListOut = this.patchedDex.openSection(patchedToc.mapList.off);
patchedToc.writeMap(mapListOut);
this.patchedDex.writeHashes();
// Finally, write patched dex to file.
this.patchedDex.writeTo(out);
}
public void executeAndSaveTo(File file) throws IOException {
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(file));
executeAndSaveTo(os);
} finally {
if (os != null) {
try {
os.close();
} catch (Exception e) {
// ignored.
}
}
}
}
}