/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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.uiautomator; import com.android.ddmlib.CollectingOutputReceiver; import com.android.ddmlib.IDevice; import com.android.ddmlib.RawImage; import com.android.ddmlib.SyncService; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.widgets.Display; import java.io.File; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class UiAutomatorHelper { public static final int UIAUTOMATOR_MIN_API_LEVEL = 16; private static final String UIAUTOMATOR = "/system/bin/uiautomator"; //$NON-NLS-1$ private static final String UIAUTOMATOR_DUMP_COMMAND = "dump"; //$NON-NLS-1$ private static final String UIDUMP_DEVICE_PATH = "/data/local/tmp/uidump.xml"; //$NON-NLS-1$ private static boolean supportsUiAutomator(IDevice device) { String apiLevelString = device.getProperty(IDevice.PROP_BUILD_API_LEVEL); int apiLevel; try { apiLevel = Integer.parseInt(apiLevelString); } catch (NumberFormatException e) { apiLevel = UIAUTOMATOR_MIN_API_LEVEL; } return apiLevel >= UIAUTOMATOR_MIN_API_LEVEL; } private static void getUiHierarchyFile(IDevice device, File dst, IProgressMonitor monitor) { if (monitor == null) { monitor = new NullProgressMonitor(); } monitor.subTask("Deleting old UI XML snapshot ..."); String command = "rm " + UIDUMP_DEVICE_PATH; try { CountDownLatch commandCompleteLatch = new CountDownLatch(1); device.executeShellCommand(command, new CollectingOutputReceiver(commandCompleteLatch)); commandCompleteLatch.await(5, TimeUnit.SECONDS); } catch (Exception e1) { // ignore exceptions while deleting stale files } monitor.subTask("Taking UI XML snapshot..."); command = String.format("%s %s %s", UIAUTOMATOR, UIAUTOMATOR_DUMP_COMMAND, UIDUMP_DEVICE_PATH); CountDownLatch commandCompleteLatch = new CountDownLatch(1); try { device.executeShellCommand(command, new CollectingOutputReceiver(commandCompleteLatch)); commandCompleteLatch.await(40, TimeUnit.SECONDS); monitor.subTask("Pull UI XML snapshot from device..."); device.getSyncService().pullFile(UIDUMP_DEVICE_PATH, dst.getAbsolutePath(), SyncService.getNullProgressMonitor()); } catch (Exception e) { throw new RuntimeException(e); } } public static UiAutomatorResult takeSnapshot(IDevice device, IProgressMonitor monitor) throws UiAutomatorException { if (monitor == null) { monitor = new NullProgressMonitor(); } monitor.subTask("Checking if device support UI Automator"); if (!supportsUiAutomator(device)) { String msg = "UI Automator requires a device with API Level " + UIAUTOMATOR_MIN_API_LEVEL; throw new UiAutomatorException(msg, null); } monitor.subTask("Creating temporary files for uiautomator results."); File tmpDir = null; File xmlDumpFile = null; File screenshotFile = null; try { tmpDir = File.createTempFile("uiautomatorviewer_", ""); tmpDir.delete(); if (!tmpDir.mkdirs()) throw new IOException("Failed to mkdir"); xmlDumpFile = File.createTempFile("dump_", ".uix", tmpDir); screenshotFile = File.createTempFile("screenshot_", ".png", tmpDir); } catch (Exception e) { String msg = "Error while creating temporary file to save snapshot: " + e.getMessage(); throw new UiAutomatorException(msg, e); } tmpDir.deleteOnExit(); xmlDumpFile.deleteOnExit(); screenshotFile.deleteOnExit(); monitor.subTask("Obtaining UI hierarchy"); try { UiAutomatorHelper.getUiHierarchyFile(device, xmlDumpFile, monitor); } catch (Exception e) { String msg = "Error while obtaining UI hierarchy XML file: " + e.getMessage(); throw new UiAutomatorException(msg, e); } UiAutomatorModel model; try { model = new UiAutomatorModel(xmlDumpFile); } catch (Exception e) { String msg = "Error while parsing UI hierarchy XML file: " + e.getMessage(); throw new UiAutomatorException(msg, e); } monitor.subTask("Obtaining device screenshot"); RawImage rawImage; try { rawImage = device.getScreenshot(); } catch (Exception e) { String msg = "Error taking device screenshot: " + e.getMessage(); throw new UiAutomatorException(msg, e); } PaletteData palette = new PaletteData( rawImage.getRedMask(), rawImage.getGreenMask(), rawImage.getBlueMask()); ImageData imageData = new ImageData(rawImage.width, rawImage.height, rawImage.bpp, palette, 1, rawImage.data); ImageLoader loader = new ImageLoader(); loader.data = new ImageData[] { imageData }; loader.save(screenshotFile.getAbsolutePath(), SWT.IMAGE_PNG); Image screenshot = new Image(Display.getDefault(), imageData); return new UiAutomatorResult(xmlDumpFile, model, screenshot); } @SuppressWarnings("serial") public static class UiAutomatorException extends Exception { public UiAutomatorException(String msg, Throwable t) { super(msg, t); } } public static class UiAutomatorResult { public final File uiHierarchy; public final UiAutomatorModel model; public final Image screenshot; public UiAutomatorResult(File uiXml, UiAutomatorModel m, Image s) { uiHierarchy = uiXml; model = m; screenshot = s; } } }