i.am.jiongxuan.deapk.Deapk.java Source code

Java tutorial

Introduction

Here is the source code for i.am.jiongxuan.deapk.Deapk.java

Source

/*
 * Deapk - A tools for "APK -> Android Project"
 * Copyright (c) 2013-2014 Jiongxuan Zhang
 *
 * 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 i.am.jiongxuan.deapk;

import i.am.jiongxuan.deapk.jd.core.Decompiler;
import i.am.jiongxuan.deapk.jd.core.DecomplieEntry;
import i.am.jiongxuan.deapk.jd.core.DecomplieEnumeration;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;

import brut.androlib.Androlib;
import brut.androlib.AndrolibException;
import brut.androlib.ApkDecoder;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.util.ExtFile;

import com.googlecode.dex2jar.Method;
import com.googlecode.dex2jar.reader.DexFileReader;
import com.googlecode.dex2jar.v3.Dex2jar;
import com.googlecode.dex2jar.v3.DexExceptionHandlerImpl;

/**
 * @author Jiongxuan Zhang
 */
public class Deapk {
    private final File mApkDir;
    private final File mProjectDir;
    private final File mClassesDexFile;
    private final File mClassesJarFile;
    private final File mClassesJarErrorFile;
    private final File mErrorFile;
    private final File mSrcDir;
    private final File mSmaliDir;

    private final GenerateProjectOperator mGenerateProjectOperator;

    public Deapk(String apkPath) {
        mApkDir = new File(apkPath);

        String apkName = FilenameUtils.getName(apkPath);
        String projectName = apkName.substring(0, apkName.lastIndexOf('.'));
        mProjectDir = new File(mApkDir.getParentFile(), projectName + "_project");

        mClassesDexFile = new File(mProjectDir, "classes.dex");
        mClassesJarFile = new File(mProjectDir, "classes.jar");
        mClassesJarErrorFile = new File(mProjectDir, "classes-error.zip");
        mErrorFile = new File(mProjectDir, "error.txt");
        mSrcDir = new File(mProjectDir, "src");
        mSmaliDir = new File(mProjectDir, "smali");

        mGenerateProjectOperator = new GenerateProjectOperator(mProjectDir);
    }

    public File getApkPath() {
        return mApkDir;
    }

    public boolean isApkExists() {
        return mApkDir.exists();
    }

    public boolean isProjectExists() {
        return mProjectDir.exists();
    }

    public Date getLastDate() {
        return new Date(mProjectDir.lastModified());
    }

    public File getProjectPath() {
        return mProjectDir;
    }

    public String getProjectNameIfExists() {
        return mGenerateProjectOperator.getProjectName();
    }

    public boolean start() {
        if (isProjectExists()) {
            System.out.println(">>> (0/5) Cleaning...");
            try {
                FileUtils.deleteDirectory(getProjectPath());
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        return decodeResources() && extractAll() && decodeToClassDex() && decodeToJavaCodes()
                && writeEclipseProjectFiles();
    }

    public boolean decodeResources() {
        System.out.println(">>> (1/5) Decompiling all resource files and smali files...");

        Logger.getLogger(AndrolibResources.class.getName()).setLevel(Level.OFF);
        Logger.getLogger(Androlib.class.getName()).setLevel(Level.OFF);
        ApkDecoder apkDecoder = new ApkDecoder();
        try {
            apkDecoder.setKeepBrokenResources(true);
            apkDecoder.setBaksmaliDebugMode(false);
            apkDecoder.setDebugMode(false);
            apkDecoder.setOutDir(mProjectDir);
            apkDecoder.setApkFile(mApkDir);
            apkDecoder.decode();
        } catch (AndrolibException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        if (mSmaliDir.exists()) {
            try {
                FileUtils.moveDirectory(mSmaliDir, mSrcDir);
            } catch (IOException e) {
                e.printStackTrace();
                // Rename the smali to src is not required, skip it if error.
            }
        }

        return true;
    }

    public boolean extractAll() {
        System.out.println(">>> (2/5) Extracting files...");
        try {
            ExtFile zipFile = new ExtFile(mApkDir);
            zipFile.getDirectory().copyToDir(mProjectDir);
            return true;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean decodeToClassDex() {
        System.out.print(">>> (3/5) Generating all Java Class files...");

        try {
            DexFileReader reader = new DexFileReader(mClassesDexFile);

            int count = reader.getClassSize();
            String remain = "(Estimate " + (count / 100) + " seconds)";
            System.out.print(remain);

            DexExceptionHandlerImpl handler = new DexExceptionHandlerImpl().skipDebug(true);

            Dex2jar.from(reader).withExceptionHandler(handler).reUseReg(true).topoLogicalSort(true).skipDebug(true)
                    .optimizeSynchronized(true).printIR(false).verbose(false).to(mClassesJarFile.toString());

            if (handler != null) {
                Map<Method, Exception> exceptions = handler.getExceptions();
                if (exceptions != null && exceptions.size() > 0) {
                    File errorFile = mClassesJarErrorFile;
                    handler.dumpException(reader, errorFile);
                }
            }

            String remove = "";
            for (int i = 0; i < remain.length(); i++) {
                remove += "\b";
            }
            System.out.println(remove);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
    }

    public boolean decodeToJavaCodes() {
        System.out.print(">>> (4/5) Generating all Java source code files...");

        Decompiler decompiler = new Decompiler(mClassesJarFile);
        ArrayList<String> failureList = new ArrayList<String>();
        DecomplieEnumeration enumeration;
        try {
            enumeration = decompiler.getEnumeration();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        PercentWritter percentWritter = new PercentWritter();
        int count = enumeration.getCount();
        int current = 0;
        while (enumeration.hasMoreElements()) {
            percentWritter.print(current, count);
            try {
                DecomplieEntry entry = (DecomplieEntry) enumeration.nextElement();
                if (entry != null) {
                    String relativePath = entry.getJavaPath();
                    File saveFile = new File(mSrcDir, relativePath);
                    File saveParentDir = saveFile.getParentFile();
                    if (!saveParentDir.exists()) {
                        saveParentDir.mkdirs();
                    }

                    String source = entry.getContent();
                    if (source != null && source != "") {
                        FileUtils.write(saveFile, source);
                    } else {
                        failureList.add(relativePath.toString());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            current++;
        }
        percentWritter.end(true);

        if (failureList.size() > 0) {
            Collections.sort(failureList);
            writeError("Unable to decompile these files:", failureList);
        }

        String printString = String.format(
                "          Decompiled %d java source files, succeed %d files, error %d files", count,
                count - failureList.size(), failureList.size());
        System.out.println(printString);

        return true;
    }

    private void writeError(String errorTitle, List<String> errors) {
        try {
            FileWriter errorWriter = new FileWriter(mErrorFile);
            errorWriter.write(errorTitle + "\r\n");

            for (String error : errors) {
                errorWriter.write("      " + error + "\r\n");
            }

            if (errorWriter != null) {
                errorWriter.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean writeEclipseProjectFiles() {
        System.out.println(">>> (5/5) Creating a new Android project...");
        mGenerateProjectOperator.generateGenDir();
        mGenerateProjectOperator.genereateProjectPropertiesFile();
        mGenerateProjectOperator.generateClassPathFile();
        mGenerateProjectOperator.generateProjectFile();

        return true;
    }
}