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