/* * Copyright (C) 2008 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.screenshot; import com.android.SdkConstants; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.IDevice; import com.android.ddmlib.Log; import com.android.ddmlib.Log.ILogOutput; import com.android.ddmlib.Log.LogLevel; import com.android.ddmlib.RawImage; import com.android.ddmlib.TimeoutException; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; /** * Connects to a device using ddmlib and dumps its event log as long as the device is connected. */ public class Screenshot { public static void main(String[] args) { boolean device = false; boolean emulator = false; String serial = null; String filepath = null; boolean landscape = false; if (args.length == 0) { printUsageAndQuit(); } // parse command line parameters. int index = 0; do { String argument = args[index++]; if ("-d".equals(argument)) { if (emulator || serial != null) { printAndExit("-d conflicts with -e and -s", false /* terminate */); } device = true; } else if ("-e".equals(argument)) { if (device || serial != null) { printAndExit("-e conflicts with -d and -s", false /* terminate */); } emulator = true; } else if ("-s".equals(argument)) { // quick check on the next argument. if (index == args.length) { printAndExit("Missing serial number after -s", false /* terminate */); } if (device || emulator) { printAndExit("-s conflicts with -d and -e", false /* terminate */); } serial = args[index++]; } else if ("-l".equals(argument)) { landscape = true; } else { // get the filepath and break. filepath = argument; // should not be any other device. if (index < args.length) { printAndExit("Too many arguments!", false /* terminate */); } } } while (index < args.length); /* * If no command-line switches and no serial number was passed on the * command-line, try to read a serial number from the shell environment. */ if (!device && !emulator && serial == null) { String envSerial = System.getenv("ANDROID_SERIAL"); if (envSerial != null) { serial = envSerial; } } if (filepath == null) { printUsageAndQuit(); } Log.setLogOutput(new ILogOutput() { @Override public void printAndPromptLog(LogLevel logLevel, String tag, String message) { System.err.println(logLevel.getStringValue() + ":" + tag + ":" + message); } @Override public void printLog(LogLevel logLevel, String tag, String message) { System.err.println(logLevel.getStringValue() + ":" + tag + ":" + message); } }); // init the lib // [try to] ensure ADB is running String adbLocation = getAdbLocation(); AndroidDebugBridge.init(false /* debugger support */); try { AndroidDebugBridge bridge = AndroidDebugBridge.createBridge( adbLocation, true /* forceNewBridge */); // we can't just ask for the device list right away, as the internal thread getting // them from ADB may not be done getting the first list. // Since we don't really want getDevices() to be blocking, we wait here manually. int count = 0; while (!bridge.hasInitialDeviceList()) { try { Thread.sleep(100); count++; } catch (InterruptedException e) { // pass } // let's not wait > 10 sec. if (count > 100) { System.err.println("Timeout getting device list!"); return; } } // now get the devices IDevice[] devices = bridge.getDevices(); if (devices.length == 0) { printAndExit("No devices found!", true /* terminate */); } IDevice target = null; if (emulator || device) { for (IDevice d : devices) { // this test works because emulator and device can't both be true at the same // time. if (d.isEmulator() == emulator) { // if we already found a valid target, we print an error and return. if (target != null) { if (emulator) { printAndExit("Error: more than one emulator launched!", true /* terminate */); } else { printAndExit("Error: more than one device connected!",true /* terminate */); } } target = d; } } } else if (serial != null) { for (IDevice d : devices) { if (serial.equals(d.getSerialNumber())) { target = d; break; } } } else { if (devices.length > 1) { printAndExit("Error: more than one emulator or device available!", true /* terminate */); } target = devices[0]; } if (target != null) { try { System.out.println("Taking screenshot from: " + target.getSerialNumber()); getDeviceImage(target, filepath, landscape); System.out.println("Success."); } catch (IOException e) { e.printStackTrace(); } } else { printAndExit("Could not find matching device/emulator.", true /* terminate */); } } finally { AndroidDebugBridge.terminate(); } } private static String getAdbLocation() { String toolsDir = System.getProperty("com.android.screenshot.bindir"); //$NON-NLS-1$ if (toolsDir == null) { return null; } File sdk = new File(toolsDir).getParentFile(); // check if adb is present in platform-tools File platformTools = new File(sdk, "platform-tools"); File adb = new File(platformTools, SdkConstants.FN_ADB); if (adb.exists()) { return adb.getAbsolutePath(); } // check if adb is present in the tools directory adb = new File(toolsDir, SdkConstants.FN_ADB); if (adb.exists()) { return adb.getAbsolutePath(); } // check if we're in the Android source tree where adb is in $ANDROID_HOST_OUT/bin/adb String androidOut = System.getenv("ANDROID_HOST_OUT"); if (androidOut != null) { String adbLocation = androidOut + File.separator + "bin" + File.separator + SdkConstants.FN_ADB; if (new File(adbLocation).exists()) { return adbLocation; } } return null; } /* * Grab an image from an ADB-connected device. */ private static void getDeviceImage(IDevice device, String filepath, boolean landscape) throws IOException { RawImage rawImage; try { rawImage = device.getScreenshot(); } catch (TimeoutException e) { printAndExit("Unable to get frame buffer: timeout", true /* terminate */); return; } catch (Exception ioe) { printAndExit("Unable to get frame buffer: " + ioe.getMessage(), true /* terminate */); return; } // device/adb not available? if (rawImage == null) return; if (landscape) { rawImage = rawImage.getRotated(); } // convert raw data to an Image BufferedImage image = new BufferedImage(rawImage.width, rawImage.height, BufferedImage.TYPE_INT_ARGB); int index = 0; int IndexInc = rawImage.bpp >> 3; for (int y = 0 ; y < rawImage.height ; y++) { for (int x = 0 ; x < rawImage.width ; x++) { int value = rawImage.getARGB(index); index += IndexInc; image.setRGB(x, y, value); } } if (!ImageIO.write(image, "png", new File(filepath))) { throw new IOException("Failed to find png writer"); } } private static void printUsageAndQuit() { // 80 cols marker: 01234567890123456789012345678901234567890123456789012345678901234567890123456789 System.out.println("Usage: screenshot2 [-d | -e | -s SERIAL] [-l] OUT_FILE"); System.out.println(""); System.out.println(" -d Uses the first device found."); System.out.println(" -e Uses the first emulator found."); System.out.println(" -s Targets the device by serial number."); System.out.println(""); System.out.println(" -l Rotate images for landscape mode."); System.out.println(""); System.exit(1); } private static void printAndExit(String message, boolean terminate) { System.out.println(message); if (terminate) { AndroidDebugBridge.terminate(); } System.exit(1); } }