/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php * * 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 com.android.ide.eclipse.adt.internal.actions; import com.android.SdkConstants; import com.android.annotations.Nullable; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; import com.android.sdklib.util.GrabProcessOutput.Wait; import com.android.utils.SdkUtils; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.IObjectActionDelegate; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.IDE; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Iterator; /** * Runs dexdump on the classes.dex of a selected project. */ public class DexDumpAction implements IObjectActionDelegate { private ISelection mSelection; @Override public void setActivePart(IAction action, IWorkbenchPart targetPart) { // pass } @Override public void run(IAction action) { if (mSelection instanceof IStructuredSelection) { for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) { Object element = it.next(); IProject project = null; if (element instanceof IProject) { project = (IProject)element; } else if (element instanceof IAdaptable) { project = (IProject)((IAdaptable)element).getAdapter(IProject.class); } if (project != null) { dexDumpProject(project); } } } } @Override public void selectionChanged(IAction action, ISelection selection) { mSelection = selection; } /** * Calls {@link #runDexDump(IProject, IProgressMonitor)} inside a job. * * @param project on which to run dexdump. */ private void dexDumpProject(final IProject project) { new Job("Dexdump") { @Override protected IStatus run(IProgressMonitor monitor) { return runDexDump(project, monitor); } }.schedule(); } /** * Runs <code>dexdump</code> on the classex.dex of the project. * Saves the output in a temporary file. * On success, opens the file in the default text editor. * * @param project on which to run dexdump. * @param monitor The job's monitor. */ private IStatus runDexDump(final IProject project, IProgressMonitor monitor) { File dstFile = null; boolean removeDstFile = true; try { if (monitor != null) { monitor.beginTask(String.format("Dump dex of %1$s", project.getName()), 2); } Sdk current = Sdk.getCurrent(); if (current == null) { AdtPlugin.printErrorToConsole(project, "DexDump: missing current SDK"); //$NON-NLS-1$ return Status.OK_STATUS; } String sdkOsPath = current.getSdkLocation(); File dexDumpFile = new File(new File(sdkOsPath, SdkConstants.FD_PLATFORM_TOOLS), SdkConstants.FN_DEXDUMP); IPath binPath = project.getFolder(SdkConstants.FD_OUTPUT).getLocation(); if (binPath == null) { AdtPlugin.printErrorToConsole(project, "DexDump: missing project /bin folder. Please compile first."); //$NON-NLS-1$ return Status.OK_STATUS; } File classesDexFile = new File(binPath.toOSString(), SdkConstants.FN_APK_CLASSES_DEX); if (!classesDexFile.exists()) { AdtPlugin.printErrorToConsole(project, "DexDump: missing classex.dex for project. Please compile first.");//$NON-NLS-1$ return Status.OK_STATUS; } try { dstFile = File.createTempFile( "dexdump_" + project.getName() + "_", //$NON-NLS-1$ //$NON-NLS-2$ ".txt"); //$NON-NLS-1$ } catch (Exception e) { AdtPlugin.logAndPrintError(e, project.getName(), "DexDump: createTempFile failed."); //$NON-NLS-1$ return Status.OK_STATUS; } // --- Exec command line and save result to dst file String[] command = new String[2]; command[0] = dexDumpFile.getAbsolutePath(); command[1] = classesDexFile.getAbsolutePath(); try { final Process process = Runtime.getRuntime().exec(command); final BufferedWriter writer = new BufferedWriter(new FileWriter(dstFile)); try { final String lineSep = SdkUtils.getLineSeparator(); int err = GrabProcessOutput.grabProcessOutput( process, Wait.WAIT_FOR_READERS, new IProcessOutput() { @Override public void out(@Nullable String line) { if (line != null) { try { writer.write(line); writer.write(lineSep); } catch (IOException ignore) {} } } @Override public void err(@Nullable String line) { if (line != null) { AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, line); } } }); if (err == 0) { // The command worked. In this case we don't remove the // temp file in the finally block. removeDstFile = false; } else { AdtPlugin.printErrorToConsole(project, "DexDump failed with code " + Integer.toString(err)); //$NON-NLS-1$ return Status.OK_STATUS; } } finally { writer.close(); } } catch (InterruptedException e) { // ? } if (monitor != null) { monitor.worked(1); } // --- Open the temp file in an editor final String dstPath = dstFile.getAbsolutePath(); AdtPlugin.getDisplay().asyncExec(new Runnable() { @Override public void run() { IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(dstPath)); if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) { IWorkbench wb = PlatformUI.getWorkbench(); IWorkbenchWindow win = wb == null ? null : wb.getActiveWorkbenchWindow(); final IWorkbenchPage page = win == null ? null : win.getActivePage(); if (page != null) { try { IDE.openEditorOnFileStore(page, fileStore); } catch (PartInitException e) { AdtPlugin.logAndPrintError(e, project.getName(), "Opening DexDump result failed. Result is available at %1$s", //$NON-NLS-1$ dstPath); } } } } }); if (monitor != null) { monitor.worked(1); } return Status.OK_STATUS; } catch (IOException e) { AdtPlugin.logAndPrintError(e, project.getName(), "DexDump failed."); //$NON-NLS-1$ return Status.OK_STATUS; } finally { // By default we remove the temp file on failure. if (removeDstFile && dstFile != null) { try { dstFile.delete(); } catch (Exception e) { AdtPlugin.logAndPrintError(e, project.getName(), "DexDump: can't delete temp file %1$s.", //$NON-NLS-1$ dstFile.getAbsoluteFile()); } } if (monitor != null) { monitor.done(); } } } }