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