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

com.reandroid.dex.sections.DexLayout Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright (C) 2022 github.com/REAndroid
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.reandroid.dex.sections;

import com.reandroid.arsc.container.FixedBlockContainer;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.common.BytesOutputStream;
import com.reandroid.dex.base.DexException;
import com.reandroid.dex.common.DexUtils;
import com.reandroid.dex.common.FullRefresh;
import com.reandroid.dex.common.SectionItem;
import com.reandroid.dex.header.Checksum;
import com.reandroid.dex.header.DexHeader;
import com.reandroid.dex.id.ClassId;
import com.reandroid.dex.id.StringId;
import com.reandroid.dex.key.Key;
import com.reandroid.dex.key.TypeKey;
import com.reandroid.dex.smali.model.SmaliClass;
import com.reandroid.utils.collection.*;
import com.reandroid.utils.io.FileUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.function.Predicate;

public class DexLayout extends FixedBlockContainer implements FullRefresh {

    private final SectionList sectionList;

    private String mSimpleName;

    private final MultiMap extendingClassMap;
    private final MultiMap interfaceMap;

    private Object mTag;

    public DexLayout() {
        super(1);
        this.sectionList = new SectionList();
        addChild(0, sectionList);

        this.extendingClassMap = new MultiMap<>();
        this.interfaceMap = new MultiMap<>();
    }

    public int getVersion(){
        return getHeader().getVersion();
    }
    public void setVersion(int version){
        getHeader().setVersion(version);
    }
    public Iterator getMarkers(){
        return Marker.parse(get(SectionType.STRING_ID));
    }

    public Iterator getSubTypes(TypeKey typeKey){
        Iterator iterator = CombiningIterator.two(getExtendingClassIds(typeKey),
                getImplementationIds(typeKey));
        return new IterableIterator(iterator) {
            @Override
            public Iterator iterator(ClassId element) {
                return CombiningIterator.two(SingleIterator.of(element), getSubTypes(element.getKey()));
            }
        };
    }
    public Iterator getExtendingClassIds(TypeKey typeKey){
        if(extendingClassMap.size() == 0){
            loadExtendingClassMap();
        }
        return this.extendingClassMap.getAll(typeKey);
    }
    public Iterator getImplementationIds(TypeKey interfaceClass){
        if(interfaceMap.size() == 0){
            loadInterfacesMap();
        }
        return this.interfaceMap.getAll(interfaceClass);
    }

    public void clear(){
        extendingClassMap.clear();
        interfaceMap.clear();
        getSectionList().clear();
    }
    private void loadExtendingClassMap(){
        MultiMap superClassMap = this.extendingClassMap;
        superClassMap.clear();
        Section section = getSectionList().getSection(SectionType.CLASS_ID);
        if(section == null) {
            return;
        }
        superClassMap.setInitialSize(section.getCount());
        for (ClassId classId : section) {
            TypeKey typeKey = classId.getSuperClassKey();
            if(!DexUtils.isJavaFramework(typeKey.getTypeName())){
                superClassMap.put(typeKey, classId);
            }
        }
    }
    private void loadInterfacesMap(){
        MultiMap interfaceMap = this.interfaceMap;
        interfaceMap.clear();
        Section section = getSectionList().getSection(SectionType.CLASS_ID);
        if(section == null) {
            return;
        }
        for (ClassId classId : section) {
            Iterator interfaceKeys = classId.getInterfaceKeys();
            while (interfaceKeys.hasNext()){
                TypeKey typeKey = interfaceKeys.next();
                if(!DexUtils.isJavaFramework(typeKey.getTypeName())) {
                    interfaceMap.put(typeKey, classId);
                }
            }
        }
    }
    public Iterator getStrings(){
        return getItems(SectionType.STRING_ID);
    }
    public Iterator getClonedItems(SectionType sectionType) {
        Section section = getSectionList().getSection(sectionType);
        if(section != null){
            return section.clonedIterator();
        }
        return EmptyIterator.of();
    }
    public Iterator getItems(SectionType sectionType) {
        Section section = getSectionList().getSection(sectionType);
        if(section != null){
            return section.iterator();
        }
        return EmptyIterator.of();
    }
    @Override
    public void refreshFull() throws DexException{
        getSectionList().refreshFull();
        Checksum checksum = getHeader().checksum;
        int previousSum = checksum.getValue();
        int max_trials = 10;
        int trials;
        for(trials = 0; trials < max_trials; trials++){
            refresh();
            int sum = checksum.getValue();
            if(previousSum == sum){
                return;
            }
            previousSum = sum;
        }
        throw new DexException("Failed to refresh trials = " + trials);
    }
    public void sortSection(SectionType[] order){
        refresh();
        getSectionList().sortSection(order);
        refresh();
    }
    public void clearPoolMap(SectionType sectionType){
        getSectionList().clearPoolMap(sectionType);
    }
    public void clearPoolMap(){
        extendingClassMap.clear();
        interfaceMap.clear();
        getSectionList().clearPoolMap();
    }
    public void sortStrings(){
        getSectionList().sortStrings();
    }

    public  Iterator getAll(SectionType sectionType, Key key){
        Section section = get(sectionType);
        if(section != null){
            return section.getAll(key);
        }
        return EmptyIterator.of();
    }
    public  boolean removeEntries(SectionType sectionType, Predicate filter){
        Section section = get(sectionType);
        if(section != null){
            return section.removeEntries(filter);
        }
        return false;
    }
    public  boolean removeWithKeys(SectionType sectionType, Predicate filter){
        Section section = get(sectionType);
        if(section != null){
            return section.removeWithKeys(filter);
        }
        return false;
    }
    public  boolean removeWithKey(SectionType sectionType, Key key){
        Section section = get(sectionType);
        if(section != null){
            return section.remove(key);
        }
        return false;
    }
    public  T1 get(SectionType sectionType, Key key){
        Section section = get(sectionType);
        if(section != null){
            return section.getSectionItem(key);
        }
        return null;
    }
    public Section get(SectionType sectionType){
        return getSectionList().getSection(sectionType);
    }
    public DexHeader getHeader() {
        return getSectionList().getHeader();
    }
    public SectionList getSectionList(){
        return sectionList;
    }
    public MapList getMapList(){
        return getSectionList().getMapList();
    }

    @Override
    protected void onPreRefresh() {
        sectionList.refresh();
        interfaceMap.clear();
        extendingClassMap.clear();
    }
    @Override
    protected void onRefreshed() {
        sectionList.updateHeader();
    }
    public boolean isEmpty(){
        Section section = get(SectionType.CLASS_ID);
        return section == null || section.getCount() == 0;
    }
    public boolean merge(MergeOptions options, ClassId classId){
        return getSectionList().merge(options, classId);
    }
    public boolean merge(MergeOptions options, DexLayout dexFile){
        if(dexFile == this){
            options.onMergeError(this, getSectionList(), "Can not merge dex file to self");
            return false;
        }
        return getSectionList().merge(options, dexFile.getSectionList());
    }
    public ClassId fromSmali(SmaliClass smaliClass) throws IOException {
        return getSectionList().fromSmali(smaliClass);
    }
    @Override
    public byte[] getBytes(){
        BytesOutputStream outputStream = new BytesOutputStream(
                getHeader().fileSize.get());
        try {
            writeBytes(outputStream);
            outputStream.close();
        } catch (IOException ignored) {
        }
        return outputStream.toByteArray();
    }

    public void read(byte[] dexBytes) throws IOException {
        BlockReader reader = new BlockReader(dexBytes);
        readBytes(reader);
        reader.close();
    }
    public void read(InputStream inputStream) throws IOException {
        BlockReader reader;
        if(inputStream instanceof BlockReader){
            reader = (BlockReader) inputStream;
        }else {
            reader = new BlockReader(inputStream);
        }
        readBytes(reader);
        reader.close();
    }
    public void readStrings(InputStream inputStream) throws IOException {
        BlockReader reader;
        if(inputStream instanceof BlockReader){
            reader = (BlockReader) inputStream;
        }else {
            reader = new BlockReader(inputStream);
        }
        readStrings(reader);
    }
    public void readStrings(BlockReader reader) throws IOException {
        readSections(reader, sectionType ->
                sectionType == SectionType.STRING_ID ||
                        sectionType == SectionType.STRING_DATA);
    }
    public void readClassIds(BlockReader reader) throws IOException {
        readSections(reader, sectionType ->
                sectionType == SectionType.STRING_ID ||
                        sectionType == SectionType.STRING_DATA||
                        sectionType == SectionType.TYPE_ID||
                        sectionType == SectionType.CLASS_ID);
    }
    public void readSections(BlockReader reader, Predicate> filter) throws IOException {
        getSectionList().readSections(reader, filter);
        reader.close();
    }
    public void read(File file) throws IOException {
        BlockReader reader = new BlockReader(file);
        readBytes(reader);
        reader.close();
    }
    public void write(File file) throws IOException {
        OutputStream outputStream = FileUtil.outputStream(file);
        writeBytes(outputStream);
        outputStream.close();
    }


    public Object getTag() {
        return mTag;
    }
    public void setTag(Object tag) {
        this.mTag = tag;
    }
    public String getSimpleName() {
        return mSimpleName;
    }
    public void setSimpleName(String simpleName) {
        this.mSimpleName = simpleName;
    }

    public static DexLayout createDefault(){
        DexLayout dexLayout = new DexLayout();
        SectionList sectionList = dexLayout.getSectionList();
        MapList mapList = sectionList.getMapList();
        mapList.getOrCreate(SectionType.HEADER);
        mapList.getOrCreate(SectionType.MAP_LIST);
        SectionType[] commonTypes = SectionType.getR8Order();
        for(SectionType sectionType : commonTypes){
            sectionList.getOrCreateSection(sectionType);
        }

        sectionList.getMapList().linkHeader(sectionList.getHeader());

        return dexLayout;
    }
    public static boolean isDexFile(File file){
        if(file == null || !file.isFile()){
            return false;
        }
        DexHeader dexHeader = null;
        try {
            InputStream inputStream = FileUtil.inputStream(file);
            dexHeader = DexHeader.readHeader(inputStream);
            inputStream.close();
        } catch (IOException ignored) {
        }
        return isDexFile(dexHeader);
    }
    public static boolean isDexFile(InputStream inputStream){
        DexHeader dexHeader = null;
        try {
            dexHeader = DexHeader.readHeader(inputStream);
            inputStream.close();
        } catch (IOException ignored) {
        }
        return isDexFile(dexHeader);
    }
    private static boolean isDexFile(DexHeader dexHeader){
        if(dexHeader == null){
            return false;
        }
        if(dexHeader.magic.isDefault()){
            return false;
        }
        int version = dexHeader.getVersion();
        return version > 0 && version < 1000;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy