Home | History | Annotate | Download | only in actions
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.actions;
     18 
     19 import com.android.SdkConstants;
     20 import com.android.annotations.Nullable;
     21 import com.android.ide.eclipse.adt.AdtPlugin;
     22 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
     23 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     24 import com.android.sdklib.BuildToolInfo;
     25 import com.android.utils.GrabProcessOutput;
     26 import com.android.utils.GrabProcessOutput.IProcessOutput;
     27 import com.android.utils.GrabProcessOutput.Wait;
     28 import com.android.utils.SdkUtils;
     29 
     30 import org.eclipse.core.filesystem.EFS;
     31 import org.eclipse.core.filesystem.IFileStore;
     32 import org.eclipse.core.resources.IProject;
     33 import org.eclipse.core.runtime.IAdaptable;
     34 import org.eclipse.core.runtime.IPath;
     35 import org.eclipse.core.runtime.IProgressMonitor;
     36 import org.eclipse.core.runtime.IStatus;
     37 import org.eclipse.core.runtime.Path;
     38 import org.eclipse.core.runtime.Status;
     39 import org.eclipse.core.runtime.jobs.Job;
     40 import org.eclipse.jface.action.IAction;
     41 import org.eclipse.jface.viewers.ISelection;
     42 import org.eclipse.jface.viewers.IStructuredSelection;
     43 import org.eclipse.ui.IObjectActionDelegate;
     44 import org.eclipse.ui.IWorkbench;
     45 import org.eclipse.ui.IWorkbenchPage;
     46 import org.eclipse.ui.IWorkbenchPart;
     47 import org.eclipse.ui.IWorkbenchWindow;
     48 import org.eclipse.ui.PartInitException;
     49 import org.eclipse.ui.PlatformUI;
     50 import org.eclipse.ui.ide.IDE;
     51 
     52 import java.io.BufferedWriter;
     53 import java.io.File;
     54 import java.io.FileWriter;
     55 import java.io.IOException;
     56 import java.util.Iterator;
     57 
     58 /**
     59  * Runs dexdump on the classes.dex of a selected project.
     60  */
     61 public class DexDumpAction implements IObjectActionDelegate {
     62 
     63     private ISelection mSelection;
     64 
     65     @Override
     66     public void setActivePart(IAction action, IWorkbenchPart targetPart) {
     67         // pass
     68     }
     69 
     70     @Override
     71     public void run(IAction action) {
     72         if (mSelection instanceof IStructuredSelection) {
     73             for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) {
     74                 Object element = it.next();
     75                 IProject project = null;
     76                 if (element instanceof IProject) {
     77                     project = (IProject)element;
     78                 } else if (element instanceof IAdaptable) {
     79                     project = (IProject)((IAdaptable)element).getAdapter(IProject.class);
     80                 }
     81                 if (project != null) {
     82                     dexDumpProject(project);
     83                 }
     84             }
     85         }
     86     }
     87 
     88     @Override
     89     public void selectionChanged(IAction action, ISelection selection) {
     90         mSelection = selection;
     91     }
     92 
     93     /**
     94      * Calls {@link #runDexDump(IProject, IProgressMonitor)} inside a job.
     95      *
     96      * @param project on which to run dexdump.
     97      */
     98     private void dexDumpProject(final IProject project) {
     99         new Job("Dexdump") {
    100             @Override
    101             protected IStatus run(IProgressMonitor monitor) {
    102                 return runDexDump(project, monitor);
    103             }
    104         }.schedule();
    105     }
    106 
    107     /**
    108      * Runs <code>dexdump</code> on the classex.dex of the project.
    109      * Saves the output in a temporary file.
    110      * On success, opens the file in the default text editor.
    111      *
    112      * @param project on which to run dexdump.
    113      * @param monitor The job's monitor.
    114      */
    115     private IStatus runDexDump(final IProject project, IProgressMonitor monitor) {
    116         File dstFile = null;
    117         boolean removeDstFile = true;
    118         try {
    119             if (monitor != null) {
    120                 monitor.beginTask(String.format("Dump dex of %1$s", project.getName()), 2);
    121             }
    122 
    123             Sdk current = Sdk.getCurrent();
    124             if (current == null) {
    125                 AdtPlugin.printErrorToConsole(project,
    126                         "DexDump: missing current SDK");                            //$NON-NLS-1$
    127                 return Status.OK_STATUS;
    128             }
    129 
    130             BuildToolInfo buildToolInfo = current.getLatestBuildTool();
    131             if (buildToolInfo == null) {
    132                 AdtPlugin.printErrorToConsole(project,
    133                     "SDK missing build tools. Please install build tools using SDK Manager.");
    134                 return Status.OK_STATUS;
    135             }
    136 
    137             File buildToolsFolder = buildToolInfo.getLocation();
    138             File dexDumpFile = new File(buildToolsFolder, SdkConstants.FN_DEXDUMP);
    139 
    140             IPath binPath = project.getFolder(SdkConstants.FD_OUTPUT).getLocation();
    141             if (binPath == null) {
    142                 AdtPlugin.printErrorToConsole(project,
    143                     "DexDump: missing project /bin folder. Please compile first."); //$NON-NLS-1$
    144                 return Status.OK_STATUS;
    145             }
    146 
    147             File classesDexFile =
    148                 new File(binPath.toOSString(), SdkConstants.FN_APK_CLASSES_DEX);
    149             if (!classesDexFile.exists()) {
    150                 AdtPlugin.printErrorToConsole(project,
    151                     "DexDump: missing classex.dex for project. Please compile first.");//$NON-NLS-1$
    152                 return Status.OK_STATUS;
    153             }
    154 
    155             try {
    156                 dstFile = File.createTempFile(
    157                         "dexdump_" + project.getName() + "_",         //$NON-NLS-1$ //$NON-NLS-2$
    158                         ".txt");                                                    //$NON-NLS-1$
    159             } catch (Exception e) {
    160                 AdtPlugin.logAndPrintError(e, project.getName(),
    161                         "DexDump: createTempFile failed.");                         //$NON-NLS-1$
    162                 return Status.OK_STATUS;
    163             }
    164 
    165             // --- Exec command line and save result to dst file
    166 
    167             String[] command = new String[2];
    168             command[0] = dexDumpFile.getAbsolutePath();
    169             command[1] = classesDexFile.getAbsolutePath();
    170 
    171             try {
    172                 final Process process = Runtime.getRuntime().exec(command);
    173 
    174                 final BufferedWriter writer = new BufferedWriter(new FileWriter(dstFile));
    175                 try {
    176                     final String lineSep = SdkUtils.getLineSeparator();
    177 
    178                     int err = GrabProcessOutput.grabProcessOutput(
    179                             process,
    180                             Wait.WAIT_FOR_READERS,
    181                             new IProcessOutput() {
    182                                 @Override
    183                                 public void out(@Nullable String line) {
    184                                     if (line != null) {
    185                                         try {
    186                                             writer.write(line);
    187                                             writer.write(lineSep);
    188                                         } catch (IOException ignore) {}
    189                                     }
    190                                 }
    191 
    192                                 @Override
    193                                 public void err(@Nullable String line) {
    194                                     if (line != null) {
    195                                         AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE,
    196                                                 project, line);
    197                                     }
    198                                 }
    199                             });
    200 
    201                     if (err == 0) {
    202                         // The command worked. In this case we don't remove the
    203                         // temp file in the finally block.
    204                         removeDstFile = false;
    205                     } else {
    206                         AdtPlugin.printErrorToConsole(project,
    207                             "DexDump failed with code " + Integer.toString(err));       //$NON-NLS-1$
    208                         return Status.OK_STATUS;
    209                     }
    210                 } finally {
    211                     writer.close();
    212                 }
    213             } catch (InterruptedException e) {
    214                 // ?
    215             }
    216 
    217             if (monitor != null) {
    218                 monitor.worked(1);
    219             }
    220 
    221             // --- Open the temp file in an editor
    222 
    223             final String dstPath = dstFile.getAbsolutePath();
    224             AdtPlugin.getDisplay().asyncExec(new Runnable() {
    225                 @Override
    226                 public void run() {
    227                     IFileStore fileStore =
    228                         EFS.getLocalFileSystem().getStore(new Path(dstPath));
    229                     if (!fileStore.fetchInfo().isDirectory() &&
    230                             fileStore.fetchInfo().exists()) {
    231 
    232                         IWorkbench wb = PlatformUI.getWorkbench();
    233                         IWorkbenchWindow win = wb == null ? null : wb.getActiveWorkbenchWindow();
    234                         final IWorkbenchPage page = win == null ? null : win.getActivePage();
    235 
    236                         if (page != null) {
    237                             try {
    238                                 IDE.openEditorOnFileStore(page, fileStore);
    239                             } catch (PartInitException e) {
    240                                 AdtPlugin.logAndPrintError(e, project.getName(),
    241                                 "Opening DexDump result failed. Result is available at %1$s", //$NON-NLS-1$
    242                                 dstPath);
    243                             }
    244                         }
    245                     }
    246                 }
    247             });
    248 
    249             if (monitor != null) {
    250                 monitor.worked(1);
    251             }
    252 
    253             return Status.OK_STATUS;
    254 
    255         } catch (IOException e) {
    256             AdtPlugin.logAndPrintError(e, project.getName(),
    257                     "DexDump failed.");                                     //$NON-NLS-1$
    258             return Status.OK_STATUS;
    259 
    260         } finally {
    261             // By default we remove the temp file on failure.
    262             if (removeDstFile && dstFile != null) {
    263                 try {
    264                     dstFile.delete();
    265                 } catch (Exception e) {
    266                     AdtPlugin.logAndPrintError(e, project.getName(),
    267                             "DexDump: can't delete temp file %1$s.",        //$NON-NLS-1$
    268                             dstFile.getAbsoluteFile());
    269                 }
    270             }
    271             if (monitor != null) {
    272                 monitor.done();
    273             }
    274         }
    275     }
    276 }
    277