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

com.tencent.tinker.commons.dexpatcher.DexPatchApplier Maven / Gradle / Ivy

Go to download

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstalling apk.

There is a newer version: 1.9.14.27
Show newest version
/*
 * 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.
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy