Add a new preload tool based on hprof dumps. This means that only a userdebug build (to adjust the pre-existing preloaded-classes file) is required now, not a recompiled runtime. Change-Id: Ib0c00de3b248e49fa8271cbace67c5d4a50170a1
225 lines
7.4 KiB
Java
225 lines
7.4 KiB
Java
/*
|
|
* Copyright (C) 2015 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.preload;
|
|
|
|
import com.android.ddmlib.AndroidDebugBridge;
|
|
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
|
|
import com.android.ddmlib.Client;
|
|
import com.android.ddmlib.IDevice;
|
|
|
|
/**
|
|
* Helper class for common communication with a Client (the ddms name for a running application).
|
|
*
|
|
* Instances take a default timeout parameter that's applied to all functions without explicit
|
|
* timeout. Timeouts are in milliseconds.
|
|
*/
|
|
public class ClientUtils {
|
|
|
|
private int defaultTimeout;
|
|
|
|
public ClientUtils() {
|
|
this(10000);
|
|
}
|
|
|
|
public ClientUtils(int defaultTimeout) {
|
|
this.defaultTimeout = defaultTimeout;
|
|
}
|
|
|
|
/**
|
|
* Shortcut for findClient with default timeout.
|
|
*/
|
|
public Client findClient(IDevice device, String processName, int processPid) {
|
|
return findClient(device, processName, processPid, defaultTimeout);
|
|
}
|
|
|
|
/**
|
|
* Find the client with the given process name or process id. The name takes precedence over
|
|
* the process id (if valid). Stop looking after the given timeout.
|
|
*
|
|
* @param device The device to communicate with.
|
|
* @param processName The name of the process. May be null.
|
|
* @param processPid The pid of the process. Values less than or equal to zero are ignored.
|
|
* @param timeout The amount of milliseconds to wait, at most.
|
|
* @return The client, if found. Otherwise null.
|
|
*/
|
|
public Client findClient(IDevice device, String processName, int processPid, int timeout) {
|
|
WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
|
|
return wfc.get();
|
|
}
|
|
|
|
/**
|
|
* Shortcut for findAllClients with default timeout.
|
|
*/
|
|
public Client[] findAllClients(IDevice device) {
|
|
return findAllClients(device, defaultTimeout);
|
|
}
|
|
|
|
/**
|
|
* Retrieve all clients known to the given device. Wait at most the given timeout.
|
|
*
|
|
* @param device The device to investigate.
|
|
* @param timeout The amount of milliseconds to wait, at most.
|
|
* @return An array of clients running on the given device. May be null depending on the
|
|
* device implementation.
|
|
*/
|
|
public Client[] findAllClients(IDevice device, int timeout) {
|
|
if (device.hasClients()) {
|
|
return device.getClients();
|
|
}
|
|
WaitForClients wfc = new WaitForClients(device, timeout);
|
|
return wfc.get();
|
|
}
|
|
|
|
private static class WaitForClient implements IClientChangeListener {
|
|
|
|
private IDevice device;
|
|
private String processName;
|
|
private int processPid;
|
|
private long timeout;
|
|
private Client result;
|
|
|
|
public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
|
|
this.device = device;
|
|
this.processName = processName;
|
|
this.processPid = processPid;
|
|
this.timeout = timeout;
|
|
this.result = null;
|
|
}
|
|
|
|
public Client get() {
|
|
synchronized (this) {
|
|
AndroidDebugBridge.addClientChangeListener(this);
|
|
|
|
// Maybe it's already there.
|
|
if (result == null) {
|
|
result = searchForClient(device);
|
|
}
|
|
|
|
if (result == null) {
|
|
try {
|
|
wait(timeout);
|
|
} catch (InterruptedException e) {
|
|
// Note: doesn't guard for spurious wakeup.
|
|
}
|
|
}
|
|
}
|
|
|
|
AndroidDebugBridge.removeClientChangeListener(this);
|
|
return result;
|
|
}
|
|
|
|
private Client searchForClient(IDevice device) {
|
|
if (processName != null) {
|
|
Client tmp = device.getClient(processName);
|
|
if (tmp != null) {
|
|
return tmp;
|
|
}
|
|
}
|
|
if (processPid > 0) {
|
|
String name = device.getClientName(processPid);
|
|
if (name != null && !name.isEmpty()) {
|
|
Client tmp = device.getClient(name);
|
|
if (tmp != null) {
|
|
return tmp;
|
|
}
|
|
}
|
|
}
|
|
if (processPid > 0) {
|
|
// Try manual search.
|
|
for (Client cl : device.getClients()) {
|
|
if (cl.getClientData().getPid() == processPid
|
|
&& cl.getClientData().getClientDescription() != null) {
|
|
return cl;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private boolean isTargetClient(Client c) {
|
|
if (processPid > 0 && c.getClientData().getPid() == processPid) {
|
|
return true;
|
|
}
|
|
if (processName != null
|
|
&& processName.equals(c.getClientData().getClientDescription())) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void clientChanged(Client arg0, int arg1) {
|
|
synchronized (this) {
|
|
if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
|
|
if (isTargetClient(arg0)) {
|
|
result = arg0;
|
|
notifyAll();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class WaitForClients implements IClientChangeListener {
|
|
|
|
private IDevice device;
|
|
private long timeout;
|
|
|
|
public WaitForClients(IDevice device, long timeout) {
|
|
this.device = device;
|
|
this.timeout = timeout;
|
|
}
|
|
|
|
public Client[] get() {
|
|
synchronized (this) {
|
|
AndroidDebugBridge.addClientChangeListener(this);
|
|
|
|
if (device.hasClients()) {
|
|
return device.getClients();
|
|
}
|
|
|
|
try {
|
|
wait(timeout); // Note: doesn't guard for spurious wakeup.
|
|
} catch (InterruptedException exc) {
|
|
}
|
|
|
|
// We will be woken up when the first client data arrives. Sleep a little longer
|
|
// to give (hopefully all of) the rest of the clients a chance to become available.
|
|
// Note: a loop with timeout is brittle as well and complicated, just accept this
|
|
// for now.
|
|
try {
|
|
Thread.sleep(500);
|
|
} catch (InterruptedException exc) {
|
|
}
|
|
}
|
|
|
|
AndroidDebugBridge.removeClientChangeListener(this);
|
|
|
|
return device.getClients();
|
|
}
|
|
|
|
@Override
|
|
public void clientChanged(Client arg0, int arg1) {
|
|
synchronized (this) {
|
|
if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
|
|
notifyAll();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|