• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
setActivePart(IAction action, IWorkbenchPart targetPart)66     public void setActivePart(IAction action, IWorkbenchPart targetPart) {
67         // pass
68     }
69 
70     @Override
run(IAction action)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
selectionChanged(IAction action, ISelection selection)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      */
dexDumpProject(final IProject project)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      */
runDexDump(final IProject project, IProgressMonitor monitor)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