package com.android.preload;
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
import com.android.preload.classdataretrieval.hprof.Hprof;
import com.android.ddmlib.DdmPreferences;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.SyncException;
import com.android.ddmlib.TimeoutException;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class DeviceUtils {
private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes";
private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE;
private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art";
private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE;
private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE;
private static final String START_SHELL_CMD = "start";
private static final String STOP_SHELL_CMD = "stop";
private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system";
private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\"";
public static void init(int debugPort) {
DdmPreferences.setSelectedDebugPort(debugPort);
Hprof.init();
AndroidDebugBridge.init(true);
AndroidDebugBridge.createBridge();
}
public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
}
public static String doShellReturnString(IDevice device, String cmdline, long timeout,
TimeUnit unit) {
CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
doShell(device, cmdline, rec, timeout, unit);
return rec.toString();
}
public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
long timeout, TimeUnit unit) {
try {
device.executeShellCommand(cmdline, receiver, timeout, unit);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void doAMStart(IDevice device, String name, String activity) {
doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
}
public static IDevice findDevice(String serial, int timeout) {
WaitForDevice wfd = new WaitForDevice(serial, timeout);
return wfd.get();
}
public static IDevice[] findDevices(int timeout) {
WaitForDevice wfd = new WaitForDevice(null, timeout);
wfd.get();
return AndroidDebugBridge.getBridge().getDevices();
}
public static String getBuildType(IDevice device) {
try {
Future<String> buildType = device.getSystemProperty("ro.build.type");
return buildType.get(500, TimeUnit.MILLISECONDS);
} catch (Exception e) {
}
return null;
}
public static boolean hasPrebuiltBootImage(IDevice device) {
String ret =
doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);
return !ret.contains("No such file or directory");
}
public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout)
throws AdbCommandRejectedException, IOException, TimeoutException, SyncException {
boolean writeEmpty = (pcFile == null);
if (writeEmpty) {
String oldContent =
doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS);
if (oldContent.trim().equals("")) {
System.out.println("Preloaded-classes already empty.");
return true;
}
}
doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS);
doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS);
doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS);
doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS);
if (writeEmpty) {
doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS);
} else {
device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE);
}
doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS);
doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS);
return waitForBootComplete(device, bootTimeout);
}
private static boolean waitForBootComplete(IDevice device, long timeout) {
Date startDate = new Date();
for (;;) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
String ret =
doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
if (ret.trim().equals("1")) {
break;
}
System.out.println("Still not booted: " + ret);
Date endDate = new Date();
long seconds =
TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
if (seconds > timeout) {
return false;
}
}
return true;
}
public static void enableTracing(IDevice device) {
doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);
doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);
doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
doShell(device, "setprop dalvik.vm.method-trace-file "
+ "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
}
private static class NullShellOutputReceiver implements IShellOutputReceiver {
@Override
public boolean isCancelled() {
return false;
}
@Override
public void flush() {}
@Override
public void addOutput(byte[] arg0, int arg1, int arg2) {}
}
private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {
private StringBuilder builder = new StringBuilder();
@Override
public String toString() {
String ret = builder.toString();
while (ret.endsWith("\r") || ret.endsWith("\n")) {
ret = ret.substring(0, ret.length() - 1);
}
return ret;
}
@Override
public void addOutput(byte[] arg0, int arg1, int arg2) {
builder.append(new String(arg0, arg1, arg2));
}
@Override
public void flush() {}
@Override
public boolean isCancelled() {
return false;
}
}
private static class WaitForDevice {
private String serial;
private long timeout;
private IDevice device;
public WaitForDevice(String serial, long timeout) {
this.serial = serial;
this.timeout = timeout;
device = null;
}
public IDevice get() {
if (device == null) {
WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
synchronized (wfdl) {
AndroidDebugBridge.addDeviceChangeListener(wfdl);
IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
if (serial != null) {
for (IDevice d : devices) {
if (serial.equals(d.getSerialNumber())) {
if (d.hasClients()) {
device = d;
}
break;
}
}
} else {
if (devices.length > 0) {
device = devices[0];
}
}
if (device == null) {
try {
wfdl.wait(timeout);
} catch (InterruptedException e) {
}
device = wfdl.getDevice();
}
AndroidDebugBridge.removeDeviceChangeListener(wfdl);
}
}
if (device != null) {
WaitForClientsListener wfcl = new WaitForClientsListener(device);
synchronized (wfcl) {
AndroidDebugBridge.addDeviceChangeListener(wfcl);
if (!device.hasClients()) {
try {
wfcl.wait(timeout);
} catch (InterruptedException e) {
}
}
AndroidDebugBridge.removeDeviceChangeListener(wfcl);
}
}
return device;
}
private static class WaitForDeviceListener implements IDeviceChangeListener {
private String serial;
private IDevice device;
public WaitForDeviceListener(String serial) {
this.serial = serial;
}
public IDevice getDevice() {
return device;
}
@Override
public void deviceChanged(IDevice arg0, int arg1) {
deviceConnected(arg0);
}
@Override
public void deviceConnected(IDevice arg0) {
if (device != null) {
return;
}
if (serial == null || serial.equals(arg0.getSerialNumber())) {
device = arg0;
synchronized (this) {
notifyAll();
}
}
}
@Override
public void deviceDisconnected(IDevice arg0) {
}
}
private static class WaitForClientsListener implements IDeviceChangeListener {
private IDevice myDevice;
public WaitForClientsListener(IDevice myDevice) {
this.myDevice = myDevice;
}
@Override
public void deviceChanged(IDevice arg0, int arg1) {
if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
synchronized (this) {
notifyAll();
}
}
}
@Override
public void deviceConnected(IDevice arg0) {
}
@Override
public void deviceDisconnected(IDevice arg0) {
}
}
}
}