chore(debugger): improve code and add details to error messages (PR #1982)

* chore: created list entry getter getRegListEntry for problematic code;
use specific containers/fields instead of SimpleEntry;
* chore: include details on the used ArtAdapter in error message
* add device info to registers exception

---------

Co-authored-by: Skylot <skylot@gmail.com>
This commit is contained in:
Jan S 2023-08-30 23:58:59 +02:00 committed by GitHub
parent f6ab105ef7
commit f695fafe74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 296 additions and 189 deletions

View File

@ -2,7 +2,7 @@ package jadx.gui.device.debugger;
public class ArtAdapter {
public interface Debugger {
public interface IArtAdapter {
int getRuntimeRegNum(int smaliNum, int regCount, int paramStart);
boolean readNullObject();
@ -10,7 +10,7 @@ public class ArtAdapter {
String typeForNull();
}
public static Debugger getAdapter(int androidReleaseVer) {
public static IArtAdapter getAdapter(int androidReleaseVer) {
if (androidReleaseVer <= 8) {
return new AndroidOreoAndBelow();
} else {
@ -18,7 +18,7 @@ public class ArtAdapter {
}
}
public static class AndroidOreoAndBelow implements Debugger {
public static class AndroidOreoAndBelow implements IArtAdapter {
@Override
public int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {
int localRegCount = regCount - paramStart;
@ -36,7 +36,7 @@ public class ArtAdapter {
}
}
public static class AndroidPieAndAbove implements Debugger {
public static class AndroidPieAndAbove implements IArtAdapter {
@Override
public int getRuntimeRegNum(int smaliNum, int regCount, int paramStart) {
return smaliNum;

View File

@ -52,7 +52,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
private JDebuggerPanel debuggerPanel;
private SmaliDebugger debugger;
private ArtAdapter.Debugger art;
private ArtAdapter.IArtAdapter art;
private final CurrentInfo cur = new CurrentInfo();
private BreakpointStore bpStore;
@ -409,7 +409,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
for (RegisterObserver.Info info : list) {
RegTreeNode reg = cur.frame.getRegNodes().get(info.getSmaliRegNum());
if (info.isLoad()) {
applyDbgInfo(reg, info.getInfo());
applyDbgInfo(reg, info.getName(), info.getType());
} else {
reg.setAlias("");
reg.setAbsoluteType(false);
@ -552,7 +552,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
} catch (SmaliDebuggerException e) {
logErr(e);
}
if (frames.size() == 0) {
if (frames.isEmpty()) {
return null;
}
List<FrameNode> frameEleList = new ArrayList<>(frames.size());
@ -671,7 +671,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
private void fetchAllRegisters(FrameNode frame) {
List<SmaliRegister> regs = cur.regAdapter.getInitializedList(frame.getCodeOffset());
for (SmaliRegister reg : regs) {
Entry<String, String> info = cur.regAdapter.getInfo(reg.getRuntimeRegNum(), frame.getCodeOffset());
RuntimeVarInfo info = cur.regAdapter.getInfo(reg.getRuntimeRegNum(), frame.getCodeOffset());
RegTreeNode regNode = frame.getRegNodes().get(reg.getRegNum());
if (info != null) {
applyDbgInfo(regNode, info);
@ -680,9 +680,13 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
}
}
private void applyDbgInfo(RegTreeNode rn, Entry<String, String> info) {
rn.setAlias(info.getKey());
rn.updateType(info.getValue());
private void applyDbgInfo(RegTreeNode rn, RuntimeVarInfo info) {
applyDbgInfo(rn, info.getName(), info.getType());
}
private void applyDbgInfo(RegTreeNode rn, String alias, String type) {
rn.setAlias(alias);
rn.updateType(type);
rn.setAbsoluteType(true);
}
@ -877,7 +881,9 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
cur.regAdapter = regAdaMap.computeIfAbsent(cur.mthFullID,
k -> RegisterObserver.merge(
getRuntimeDebugInfo(cur.frame),
getRegisterList()));
getSmaliRegisterList(),
art,
cur.mthFullID));
if (cur.smali.getRegCount(cur.mthFullID) > 0) {
updateAllRegisters(cur.frame);
@ -889,7 +895,7 @@ public final class DebugController implements SmaliDebugger.SuspendListener, IDe
});
}
private List<SmaliRegister> getRegisterList() {
private List<SmaliRegister> getSmaliRegisterList() {
int regCount = cur.smali.getRegCount(cur.mthFullID);
int paramStart = cur.smali.getParamRegStart(cur.mthFullID);
List<SmaliRegister> srs = cur.smali.getRegisterList(cur.mthFullID);

View File

@ -0,0 +1,158 @@
package jadx.gui.device.debugger;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.core.utils.StringUtils;
import jadx.gui.device.protocol.ADB;
import jadx.gui.device.protocol.ADBDevice;
import jadx.gui.utils.NLS;
public class DebugSettings {
private static final Logger LOG = LoggerFactory.getLogger(DebugSettings.class);
private static final int FORWARD_TCP_PORT = 33233;
public static final DebugSettings INSTANCE = new DebugSettings();
private int ver;
private String pid;
private String name;
private ADBDevice device;
private int forwardTcpPort = FORWARD_TCP_PORT;
private String expectPkg = "";
private boolean autoAttachPkg = false;
private DebugSettings() {
}
public void set(ADBDevice device, int ver, String pid, String name) {
this.ver = ver;
this.pid = pid;
this.name = name;
this.device = device;
this.autoAttachPkg = false;
this.expectPkg = "";
}
public DebugSettings setPid(String pid) {
this.pid = pid;
return this;
}
public DebugSettings setName(String name) {
this.name = name;
return this;
}
public String forwardJDWP() {
int localPort = forwardTcpPort;
String resultDesc = "";
try {
do {
ADBDevice.ForwardResult rst = device.forwardJDWP(localPort + "", pid);
if (rst.state == 0) {
forwardTcpPort = localPort;
return "";
}
if (rst.state == 1) {
if (rst.desc.contains("Only one usage of each socket address")) { // port is taken by other process
if (localPort < 65536) {
localPort++; // retry
continue;
}
}
}
resultDesc = rst.desc;
break;
} while (true);
} catch (Exception e) {
LOG.error("JDWP forward error", e);
}
if (StringUtils.isEmpty(resultDesc)) {
resultDesc = NLS.str("adb_dialog.forward_fail");
}
return resultDesc;
}
// we have to remove all ports that forwarding the jdwp:pid, otherwise our JDWP handshake may fail.
public void clearForward() {
String jdwpPid = " jdwp:" + pid;
String tcpPort = " tcp:" + forwardTcpPort;
try {
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
device.getDeviceInfo().getAdbPort());
for (String s : list) {
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid) && !s.contains(tcpPort)) {
String[] fields = s.split("\\s+");
for (String field : fields) {
if (field.startsWith("tcp:")) {
try {
device.removeForward(field.substring("tcp:".length()));
} catch (Exception e) {
LOG.error("JDWP remove forward error", e);
}
}
}
}
}
} catch (Exception e) {
LOG.error("JDWP clear forward error", e);
}
}
public boolean isBeingDebugged() {
String jdwpPid = " jdwp:" + pid;
String tcpPort = " tcp:" + forwardTcpPort;
try {
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
device.getDeviceInfo().getAdbPort());
for (String s : list) {
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid)) {
return !s.contains(tcpPort);
}
}
} catch (Exception e) {
LOG.error("ADB list forward error", e);
}
return false;
}
public int getVer() {
return ver;
}
public String getPid() {
return pid;
}
public String getName() {
return name;
}
public ADBDevice getDevice() {
return device;
}
public int getForwardTcpPort() {
return forwardTcpPort;
}
public String getExpectPkg() {
return expectPkg;
}
public void setExpectPkg(String expectPkg) {
this.expectPkg = expectPkg;
}
public boolean isAutoAttachPkg() {
return autoAttachPkg;
}
public void setAutoAttachPkg(boolean autoAttachPkg) {
this.autoAttachPkg = autoAttachPkg;
}
}

View File

@ -1,48 +1,50 @@
package jadx.gui.device.debugger;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import io.reactivex.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.instructions.args.ArgType;
import jadx.gui.device.debugger.SmaliDebugger.RuntimeVarInfo;
import jadx.gui.device.debugger.smali.RegisterInfo;
import jadx.gui.device.debugger.smali.SmaliRegister;
public class RegisterObserver {
private Map<Long, List<Info>> infoMap;
private final List<Entry<SmaliRegister, List<RuntimeVarInfo>>> regList;
private final List<SmaliRegisterMapping> regList;
private final ArtAdapter.IArtAdapter art;
private final String mthFullID;
private boolean hasDbgInfo = false;
private RegisterObserver() {
regList = new ArrayList<>();
infoMap = Collections.emptyMap();
private RegisterObserver(ArtAdapter.IArtAdapter art, String mthFullID) {
this.regList = new ArrayList<>();
this.infoMap = Collections.emptyMap();
this.art = art;
this.mthFullID = mthFullID;
}
public static RegisterObserver merge(List<RuntimeVarInfo> rtRegs, List<SmaliRegister> smaliRegs) {
RegisterObserver adapter = new RegisterObserver();
adapter.hasDbgInfo = rtRegs.size() > 0;
@NotNull
public static RegisterObserver merge(List<RuntimeVarInfo> rtRegs, List<SmaliRegister> smaliRegs, ArtAdapter.IArtAdapter art,
String mthFullID) {
RegisterObserver adapter = new RegisterObserver(art, mthFullID);
adapter.hasDbgInfo = !rtRegs.isEmpty();
if (adapter.hasDbgInfo) {
adapter.infoMap = new HashMap<>();
}
for (SmaliRegister sr : smaliRegs) {
adapter.regList.add(new SimpleEntry<>(sr, Collections.emptyList()));
adapter.regList.add(new SmaliRegisterMapping(sr));
}
adapter.regList.sort(Comparator.comparingInt(r -> r.getKey().getRuntimeRegNum()));
adapter.regList.sort(Comparator.comparingInt(r -> r.getSmaliRegister().getRuntimeRegNum()));
for (RuntimeVarInfo rt : rtRegs) {
Entry<SmaliRegister, List<RuntimeVarInfo>> entry = adapter.regList.get(rt.getRegNum());
if (entry.getValue().isEmpty()) {
entry.setValue(new ArrayList<>());
}
entry.getValue().add(rt);
final SmaliRegisterMapping smaliRegMapping = adapter.getRegListEntry(rt.getRegNum());
final SmaliRegister smaliReg = smaliRegMapping.getSmaliRegister();
smaliRegMapping.addRuntimeVarInfo(rt);
String type = rt.getSignature();
if (type.isEmpty()) {
@ -52,9 +54,8 @@ public class RegisterObserver {
if (at != null) {
type = at.toString();
}
Info load = new Info(entry.getKey().getRegNum(), true,
new SimpleEntry<>(rt.getName(), type));
Info unload = new Info(entry.getKey().getRegNum(), false, null);
Info load = new Info(smaliReg.getRegNum(), true, rt.getName(), type);
Info unload = new Info(smaliReg.getRegNum(), false, null, null);
adapter.infoMap.computeIfAbsent((long) rt.getStartOffset(), k -> new ArrayList<>())
.add(load);
adapter.infoMap.computeIfAbsent((long) rt.getEndOffset(), k -> new ArrayList<>())
@ -63,33 +64,53 @@ public class RegisterObserver {
return adapter;
}
@NotNull
public List<SmaliRegister> getInitializedList(long codeOffset) {
List<SmaliRegister> ret = Collections.emptyList();
for (Entry<SmaliRegister, List<RuntimeVarInfo>> info : regList) {
if (info.getKey().isInitialized(codeOffset)) {
for (SmaliRegisterMapping smaliRegisterMapping : regList) {
if (smaliRegisterMapping.getSmaliRegister().isInitialized(codeOffset)) {
if (ret.isEmpty()) {
ret = new ArrayList<>();
}
ret.add(info.getKey());
ret.add(smaliRegisterMapping.getSmaliRegister());
}
}
return ret;
}
@Nullable
public Entry<String, String> getInfo(int runtimeNum, long codeOffset) {
Entry<SmaliRegister, List<RuntimeVarInfo>> list = regList.get(runtimeNum);
for (RegisterInfo info : list.getValue()) {
public RuntimeVarInfo getInfo(int runtimeNum, long codeOffset) {
SmaliRegisterMapping list = getRegListEntry(runtimeNum);
for (RuntimeVarInfo info : list.getRuntimeVarInfoList()) {
if (info.getStartOffset() > codeOffset) {
break;
}
if (info.isInitialized(codeOffset)) {
return new SimpleEntry<>(info.getName(), info.getType());
return info;
}
}
return null;
}
private SmaliRegisterMapping getRegListEntry(int regNum) {
try {
return regList.get(regNum);
} catch (IndexOutOfBoundsException e) {
throw new RuntimeException(
String.format("Register %d does not exist (size: %d).\n %s\n Method: %s",
regNum, regList.size(), buildDeviceInfo(), mthFullID),
e);
}
}
private String buildDeviceInfo() {
DebugSettings debugSettings = DebugSettings.INSTANCE;
return "Device: " + debugSettings.getDevice().getDeviceInfo()
+ ", Android: " + debugSettings.getVer()
+ ", ArtAdapter: " + art.getClass().getSimpleName();
}
@NotNull
public List<Info> getInfoAt(long codeOffset) {
if (hasDbgInfo) {
List<Info> list = infoMap.get(codeOffset);
@ -100,15 +121,43 @@ public class RegisterObserver {
return Collections.emptyList();
}
public static class SmaliRegisterMapping {
private final SmaliRegister smaliRegister;
private List<RuntimeVarInfo> rtList = Collections.emptyList();
public SmaliRegisterMapping(SmaliRegister smaliRegister) {
this.smaliRegister = smaliRegister;
}
public SmaliRegister getSmaliRegister() {
return smaliRegister;
}
@NotNull
public List<RuntimeVarInfo> getRuntimeVarInfoList() {
return rtList;
}
public void addRuntimeVarInfo(RuntimeVarInfo rt) {
if (rtList.isEmpty()) {
rtList = new ArrayList<>();
}
rtList.add(rt);
}
}
public static class Info {
private final int smaliRegNum;
private final boolean load;
private final Entry<String, String> info;
private final String name;
private final String type;
private Info(int smaliRegNum, boolean load, Entry<String, String> info) {
private Info(int smaliRegNum, boolean load, String name, String type) {
this.smaliRegNum = smaliRegNum;
this.load = load;
this.info = info;
this.name = name;
this.type = type;
}
public int getSmaliRegNum() {
@ -119,8 +168,12 @@ public class RegisterObserver {
return load;
}
public Entry<String, String> getInfo() {
return info;
public String getName() {
return name;
}
public String getType() {
return type;
}
}
}

View File

@ -17,6 +17,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -71,7 +72,6 @@ import io.github.skylot.jdwp.JDWP.VirtualMachine.AllClassesWithGeneric.AllClasse
import io.github.skylot.jdwp.JDWP.VirtualMachine.AllThreads.AllThreadsReplyData;
import io.github.skylot.jdwp.JDWP.VirtualMachine.AllThreads.AllThreadsReplyDataThreads;
import io.github.skylot.jdwp.JDWP.VirtualMachine.CreateString.CreateStringReplyData;
import io.reactivex.annotations.NonNull;
import jadx.api.plugins.input.data.AccessFlags;
import jadx.gui.device.debugger.smali.RegisterInfo;
@ -1292,7 +1292,7 @@ public class SmaliDebugger {
return gen;
}
@NonNull
@NotNull
@Override
public String getSignature() {
return this.slot.genericSignature.trim();

View File

@ -41,6 +41,7 @@ import org.slf4j.LoggerFactory;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.device.debugger.DbgUtils;
import jadx.gui.device.debugger.DebugSettings;
import jadx.gui.device.protocol.ADB;
import jadx.gui.device.protocol.ADBDevice;
import jadx.gui.device.protocol.ADBDeviceInfo;
@ -51,15 +52,12 @@ import jadx.gui.utils.NLS;
import jadx.gui.utils.SystemInfo;
import jadx.gui.utils.UiUtils;
import static jadx.gui.device.protocol.ADBDevice.ForwardResult;
public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.JDWPProcessListener {
private static final Logger LOG = LoggerFactory.getLogger(ADBDialog.class);
private static final long serialVersionUID = -1111111202102181630L;
private static final ImageIcon ICON_DEVICE = UiUtils.openSvgIcon("adb/androidDevice");
private static final ImageIcon ICON_PROCESS = UiUtils.openSvgIcon("adb/addToWatch");
private static DebugSetting debugSetter = null;
private final transient MainWindow mainWindow;
private transient Label tipLabel;
@ -75,15 +73,12 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
public ADBDialog(MainWindow mainWindow) {
super(mainWindow);
this.mainWindow = mainWindow;
if (debugSetter == null) {
debugSetter = new DebugSetting();
}
initUI();
pathTextField.setText(mainWindow.getSettings().getAdbDialogPath());
hostTextField.setText(mainWindow.getSettings().getAdbDialogHost());
portTextField.setText(mainWindow.getSettings().getAdbDialogPort());
if (pathTextField.getText().equals("")) {
if (pathTextField.getText().isEmpty()) {
detectADBPath();
} else {
pathTextField.setText("");
@ -329,7 +324,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
if (!setupArgs(deviceNode.device, pid, (String) node.getUserObject())) {
return;
}
if (debugSetter.isBeingDebugged()) {
if (DebugSettings.INSTANCE.isBeingDebugged()) {
if (JOptionPane.showConfirmDialog(mainWindow,
NLS.str("adb_dialog.being_debugged_msg"),
NLS.str("adb_dialog.being_debugged_title"),
@ -347,23 +342,21 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
}
private static boolean attachProcess(MainWindow mainWindow) {
if (debugSetter == null) {
return false;
}
debugSetter.clearForward();
String rst = debugSetter.forwardJDWP();
DebugSettings debugSettings = DebugSettings.INSTANCE;
debugSettings.clearForward();
String rst = debugSettings.forwardJDWP();
if (!rst.isEmpty()) {
UiUtils.showMessageBox(mainWindow, rst);
return false;
}
try {
return mainWindow.getDebuggerPanel().showDebugger(
debugSetter.name,
debugSetter.device.getDeviceInfo().getAdbHost(),
debugSetter.forwardTcpPort,
debugSetter.ver,
debugSetter.device,
debugSetter.pid);
debugSettings.getName(),
debugSettings.getDevice().getDeviceInfo().getAdbHost(),
debugSettings.getForwardTcpPort(),
debugSettings.getVer(),
debugSettings.getDevice(),
debugSettings.getPid());
} catch (Exception e) {
LOG.error("Failed to attach to process", e);
return false;
@ -371,18 +364,17 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
}
public static boolean launchForDebugging(MainWindow mainWindow, String fullAppPath, boolean autoAttach) {
if (debugSetter != null) {
debugSetter.autoAttachPkg = autoAttach;
try {
int pid = debugSetter.device.launchApp(fullAppPath);
if (pid != -1) {
debugSetter.setPid(String.valueOf(pid))
.setName(fullAppPath);
return attachProcess(mainWindow);
}
} catch (Exception e) {
LOG.error("Failed to launch app", e);
DebugSettings debugSettings = DebugSettings.INSTANCE;
debugSettings.setAutoAttachPkg(autoAttach);
try {
int pid = debugSettings.getDevice().launchApp(fullAppPath);
if (pid != -1) {
debugSettings.setPid(String.valueOf(pid))
.setName(fullAppPath);
return attachProcess(mainWindow);
}
} catch (Exception e) {
LOG.error("Failed to launch app", e);
}
return false;
}
@ -458,7 +450,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
procs = Collections.emptyList();
}
List<String> procList = new ArrayList<>(id.size());
if (procs.size() == 0) {
if (procs.isEmpty()) {
procList.addAll(id);
} else {
for (ADB.Process proc : procs) {
@ -479,12 +471,13 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
SwingUtilities.invokeLater(() -> {
node.tNode.removeAllChildren();
DefaultMutableTreeNode foundNode = null;
DebugSettings debugSettings = DebugSettings.INSTANCE;
for (String procStr : procList) {
DefaultMutableTreeNode pnode = new DefaultMutableTreeNode(procStr);
node.tNode.add(pnode);
if (!debugSetter.expectPkg.isEmpty() && procStr.endsWith(debugSetter.expectPkg)) {
if (debugSetter.autoAttachPkg && debugSetter.device.equals(node.device)) {
debugSetter.set(node.device, debugSetter.ver, getPid(procStr), procStr);
if (!debugSettings.getExpectPkg().isEmpty() && procStr.endsWith(debugSettings.getExpectPkg())) {
if (debugSettings.isAutoAttachPkg() && debugSettings.getDevice().equals(node.device)) {
debugSettings.set(node.device, debugSettings.getVer(), getPid(procStr), procStr);
if (attachProcess(mainWindow)) {
dispose();
return;
@ -504,7 +497,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
}
private void launchApp() {
if (deviceNodes.size() == 0) {
if (deviceNodes.isEmpty()) {
UiUtils.showMessageBox(mainWindow, NLS.str("adb_dialog.no_devices"));
return;
}
@ -533,13 +526,13 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
if (pkg.isEmpty()) {
return false;
}
debugSetter.expectPkg = " " + pkg;
DebugSettings.INSTANCE.setExpectPkg(" " + pkg);
for (int i = 0; i < procTreeRoot.getChildCount(); i++) {
DefaultMutableTreeNode rn = (DefaultMutableTreeNode) procTreeRoot.getChildAt(i);
for (int j = 0; j < rn.getChildCount(); j++) {
DefaultMutableTreeNode n = (DefaultMutableTreeNode) rn.getChildAt(j);
String pName = (String) n.getUserObject();
if (pName.endsWith(debugSetter.expectPkg)) {
if (pName.endsWith(DebugSettings.INSTANCE.getExpectPkg())) {
TreePath path = new TreePath(n.getPath());
procTree.scrollPathToVisible(path);
procTree.setSelectionPath(path);
@ -594,7 +587,7 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
ver = "8";
}
ver = getMajorVer(ver);
debugSetter.set(device, Integer.parseInt(ver), pid, name);
DebugSettings.INSTANCE.set(device, Integer.parseInt(ver), pid, name);
return true;
}
@ -605,107 +598,4 @@ public class ADBDialog extends JDialog implements ADB.DeviceStateListener, ADB.J
}
return ver;
}
private class DebugSetting {
private static final int FORWARD_TCP_PORT = 33233;
private int ver;
private String pid;
private String name;
private ADBDevice device;
private int forwardTcpPort = FORWARD_TCP_PORT;
private String expectPkg = "";
private boolean autoAttachPkg = false;
private void set(ADBDevice device, int ver, String pid, String name) {
this.ver = ver;
this.pid = pid;
this.name = name;
this.device = device;
this.autoAttachPkg = false;
this.expectPkg = "";
}
private DebugSetting setPid(String pid) {
this.pid = pid;
return this;
}
private DebugSetting setName(String name) {
this.name = name;
return this;
}
private String forwardJDWP() {
int localPort = forwardTcpPort;
String resultDesc = "";
try {
do {
ForwardResult rst = device.forwardJDWP(localPort + "", pid);
if (rst.state == 0) {
forwardTcpPort = localPort;
return "";
}
if (rst.state == 1) {
if (rst.desc.contains("Only one usage of each socket address")) { // port is taken by other process
if (localPort < 65536) {
localPort++; // retry
continue;
}
}
}
resultDesc = rst.desc;
break;
} while (true);
} catch (Exception e) {
LOG.error("JDWP forward error", e);
}
if (StringUtils.isEmpty(resultDesc)) {
resultDesc = NLS.str("adb_dialog.forward_fail");
}
return resultDesc;
}
// we have to remove all ports that forwarding the jdwp:pid, otherwise our JDWP handshake may fail.
private void clearForward() {
String jdwpPid = " jdwp:" + pid;
String tcpPort = " tcp:" + forwardTcpPort;
try {
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
device.getDeviceInfo().getAdbPort());
for (String s : list) {
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid) && !s.contains(tcpPort)) {
String[] fields = s.split("\\s+");
for (String field : fields) {
if (field.startsWith("tcp:")) {
try {
device.removeForward(field.substring("tcp:".length()));
} catch (Exception e) {
LOG.error("JDWP remove forward error", e);
}
}
}
}
}
} catch (Exception e) {
LOG.error("JDWP clear forward error", e);
}
}
private boolean isBeingDebugged() {
String jdwpPid = " jdwp:" + pid;
String tcpPort = " tcp:" + forwardTcpPort;
try {
List<String> list = ADB.listForward(device.getDeviceInfo().getAdbHost(),
device.getDeviceInfo().getAdbPort());
for (String s : list) {
if (s.startsWith(device.getSerial()) && s.endsWith(jdwpPid)) {
return !s.contains(tcpPort);
}
}
} catch (Exception e) {
LOG.error("ADB list forward error", e);
}
return false;
}
}
}