mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-22 17:39:19 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dbbfa8344 | ||
|
|
669ed81f39 |
@@ -1,5 +1,10 @@
|
||||
# Change Log
|
||||
|
||||
### version 1.3.0
|
||||
* Support `ADB_SERVER_SOCKET`, `ANDROID_ADB_SERVER_ADDRESS` & `ANDROID_ADB_SERVER_PORT` env vars when connecting to ADB.
|
||||
* Replace `adbPort` configuration option with a new `adbSocket` value to allow ADB server host to be overidden. (`adbPort` is now deprecated).
|
||||
* Allow the JDWP local port to be fixed using a new `jdwpPort` configuration option.
|
||||
|
||||
### version 1.2.1
|
||||
* Java Intellisense: automatically import dependencies of AndroidX libraries.
|
||||
* Debugger: Warn about open instances of Android Studio
|
||||
|
||||
@@ -49,9 +49,9 @@ The following settings are used to configure the debugger:
|
||||
// Fully qualified path to the built APK (Android Application Package).
|
||||
"apkFile": "${workspaceRoot}/app/build/outputs/apk/app-debug.apk",
|
||||
|
||||
// Port number to connect to the local ADB (Android Debug Bridge) instance.
|
||||
// Default: 5037
|
||||
"adbPort": 5037,
|
||||
// `host:port` configuration for connecting to the ADB (Android Debug Bridge) server instance.
|
||||
// Default: localhost:5037
|
||||
"adbSocket": "localhost:5037",
|
||||
|
||||
// Automatically launch 'adb start-server' if not already started.
|
||||
// Default: true
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "android-dev-ext",
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
32
package.json
32
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "android-dev-ext",
|
||||
"displayName": "Android",
|
||||
"description": "Android debugging support for VS Code",
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.1",
|
||||
"publisher": "adelphes",
|
||||
"preview": true,
|
||||
"license": "MIT",
|
||||
@@ -238,8 +238,7 @@
|
||||
"launch": {
|
||||
"required": [
|
||||
"appSrcRoot",
|
||||
"apkFile",
|
||||
"adbPort"
|
||||
"apkFile"
|
||||
],
|
||||
"properties": {
|
||||
"amStartArgs": {
|
||||
@@ -265,12 +264,17 @@
|
||||
},
|
||||
"adbPort": {
|
||||
"type": "integer",
|
||||
"description": "Port number to connect to the local ADB (Android Debug Bridge) instance. Default: 5037",
|
||||
"description": "Port number to connect to the local ADB (Android Debug Bridge) instance. Default: 5037\nDeprecated: Configure the 'adbSocket' property instead.",
|
||||
"default": 5037
|
||||
},
|
||||
"adbSocket": {
|
||||
"type": "string",
|
||||
"description": "`host : port` configuration for connecting to the ADB (Android Debug Bridge) server instance. Default: \"localhost:5037\"",
|
||||
"default": "localhost:5037"
|
||||
},
|
||||
"autoStartADB": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically launch 'adb start-server' if not already started. Default: true",
|
||||
"description": "Automatically attempt to launch 'adb start-server' if not already started. Default: true",
|
||||
"default": true
|
||||
},
|
||||
"callStackDisplaySize": {
|
||||
@@ -278,6 +282,11 @@
|
||||
"description": "Number of entries to display in call stack views (for locations outside of the project source). 0 shows the entire call stack. Default: 1",
|
||||
"default": 1
|
||||
},
|
||||
"jdwpPort": {
|
||||
"type": "integer",
|
||||
"description": "Manually specify the local port used for connecting to the on-device debugger client.\nThis can be useful if you are using port-forwarding to connect to a remote device.\nThe specified port must be available and different from the ADB socket port.\nSet to 0 for automatic (dynamic) assignment.\nDefault: 0",
|
||||
"default": 0
|
||||
},
|
||||
"launchActivity": {
|
||||
"type": "string",
|
||||
"description": "Manually specify the activity to run when the app is started.",
|
||||
@@ -325,7 +334,6 @@
|
||||
"attach": {
|
||||
"required": [
|
||||
"appSrcRoot",
|
||||
"adbPort",
|
||||
"processId"
|
||||
],
|
||||
"properties": {
|
||||
@@ -336,9 +344,19 @@
|
||||
},
|
||||
"adbPort": {
|
||||
"type": "integer",
|
||||
"description": "Port number to connect to the local ADB (Android Debug Bridge) instance. Default: 5037",
|
||||
"description": "Port number to connect to the local ADB (Android Debug Bridge) instance. Default: 5037\nDeprecated: Configure the 'adbSocket' property instead.",
|
||||
"default": 5037
|
||||
},
|
||||
"adbSocket": {
|
||||
"type": "string",
|
||||
"description": "`host : port` configuration for connecting to the ADB (Android Debug Bridge) server instance. Default: \"localhost:5037\"",
|
||||
"default": "localhost:5037"
|
||||
},
|
||||
"jdwpPort": {
|
||||
"type": "integer",
|
||||
"description": "Manually specify the local port used for connecting to the on-device debugger client.\nThis can be useful if you are using port-forwarding to connect to a remote device.\nThe specified port must be available and different from the ADB socket port.\nSet to 0 for automatic (dynamic) assignment.\nDefault: 0",
|
||||
"default": 0
|
||||
},
|
||||
"processId": {
|
||||
"type": "string",
|
||||
"description": "PID of process to attach to.\n\"${command:PickAndroidProcess}\" will display a list of debuggable PIDs to choose from during launch.",
|
||||
|
||||
@@ -36,17 +36,94 @@ function parse_device_list(data, extended = false) {
|
||||
return devicelist;
|
||||
}
|
||||
|
||||
let adbSocketParams;
|
||||
/**
|
||||
* Return the host and port for connecting to the ADB server
|
||||
*/
|
||||
function getADBSocketParams() {
|
||||
// this is memoized to prevent alterations once the debug session is up and running
|
||||
if (adbSocketParams) {
|
||||
return adbSocketParams;
|
||||
}
|
||||
|
||||
return adbSocketParams = getIntialADBSocketParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the socket parameters for connecting to an ADB server instance.
|
||||
|
||||
* In priority order (highest first):
|
||||
* 1. adbSocket debug configuration value
|
||||
* 2. non-default adbPort debug configuration value (using localhost)
|
||||
* 3. ADB_SERVER_SOCKET environment variable
|
||||
* 4. ANDROID_ADB_SERVER_ADDRESS / ANDROID_ADB_SERVER_PORT environment variables
|
||||
* 5. [localhost]:5037
|
||||
*/
|
||||
function getIntialADBSocketParams() {
|
||||
|
||||
/**
|
||||
* Retrieve a trimmed environment variable or return a blank string
|
||||
* @param {string} name
|
||||
*/
|
||||
function envValue(name) {
|
||||
return (process.env[name] || '').trim();
|
||||
}
|
||||
|
||||
function decode_port_string(s) {
|
||||
if (!/^\d+$/.test(s)) {
|
||||
return;
|
||||
}
|
||||
const portnum = parseInt(s, 10);
|
||||
if (portnum < 1 || portnum > 65535) {
|
||||
return;
|
||||
}
|
||||
return portnum;
|
||||
}
|
||||
|
||||
const default_host = '', default_port = 5037;
|
||||
|
||||
// the ADBSocket.HostPort value is automatically set with adbSocket/adbPort values from
|
||||
// the debug configuration when the debugger session starts.
|
||||
let socket_str = ADBSocket.HostPort.trim();
|
||||
|
||||
if (socket_str !== ADBSocket.DefaultHostPort) {
|
||||
// non-default debug configuration values are configured (1. or 2.)
|
||||
const [host, port] = socket_str.split(':');
|
||||
return {
|
||||
host,
|
||||
port: decode_port_string(port) || default_port
|
||||
}
|
||||
}
|
||||
|
||||
// ADB_SERVER_SOCKET=tcp:<host>:<port>
|
||||
const adb_server_socket_match = envValue('ADB_SERVER_SOCKET').match(/^\s*tcp(?::(.*))?(?::(\d+))\s*$/);
|
||||
if (adb_server_socket_match) {
|
||||
return {
|
||||
host: adb_server_socket_match[1] || default_host,
|
||||
port: decode_port_string(adb_server_socket_match[2]) || default_port,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
host: envValue('ANDROID_ADB_SERVER_ADDRESS') || default_host,
|
||||
port: decode_port_string(envValue('ANDROID_ADB_SERVER_PORT')) || default_port,
|
||||
}
|
||||
}
|
||||
|
||||
class ADBClient {
|
||||
|
||||
/**
|
||||
* @param {string} [deviceid]
|
||||
* @param {number} [adbPort] the port number to connect to ADB
|
||||
* @param {number} [adbHost] the hostname/ip address to connect to ADB
|
||||
*/
|
||||
constructor(deviceid, adbPort = ADBSocket.ADBPort) {
|
||||
constructor(deviceid, adbPort, adbHost) {
|
||||
this.deviceid = deviceid;
|
||||
this.adbsocket = null;
|
||||
this.jdwp_socket = null;
|
||||
this.adbPort = adbPort;
|
||||
const default_adb_socket = getADBSocketParams();
|
||||
this.adbHost = adbHost || default_adb_socket.host;
|
||||
this.adbPort = adbPort || default_adb_socket.port;
|
||||
}
|
||||
|
||||
async test_adb_connection() {
|
||||
@@ -170,7 +247,10 @@ class ADBClient {
|
||||
// note that upon success, this method does not close the connection (it must be left open for
|
||||
// future commands to be sent over the jdwp socket)
|
||||
this.jdwp_socket = new JDWPSocket(o.onreply, o.ondisconnect);
|
||||
await this.jdwp_socket.connect(o.localport)
|
||||
// assume the 'local' port (routed to connect to the process on the device)
|
||||
// is set up on the same host that the adb server is running on
|
||||
const adb_server_socket = getADBSocketParams();
|
||||
await this.jdwp_socket.connect(o.localport, adb_server_socket.host);
|
||||
await this.jdwp_socket.start();
|
||||
return true;
|
||||
}
|
||||
@@ -276,12 +356,9 @@ class ADBClient {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} [hostname]
|
||||
*/
|
||||
connect_to_adb(hostname = '127.0.0.1') {
|
||||
connect_to_adb() {
|
||||
this.adbsocket = new ADBSocket();
|
||||
return this.adbsocket.connect(this.adbPort, hostname);
|
||||
return this.adbsocket.connect(this.adbPort, this.adbHost);
|
||||
}
|
||||
|
||||
disconnect_from_adb () {
|
||||
@@ -290,3 +367,4 @@ class ADBClient {
|
||||
};
|
||||
|
||||
exports.ADBClient = ADBClient;
|
||||
exports.getADBSocketParams = getADBSocketParams;
|
||||
|
||||
@@ -325,8 +325,11 @@ class AndroidDebugSession extends DebugSession {
|
||||
|
||||
/**
|
||||
* @typedef AndroidAttachArguments
|
||||
* @property {number} adbPort
|
||||
* @property {string} adbSocket
|
||||
* @property {string} appSrcRoot
|
||||
* @property {boolean} autoStartADB
|
||||
* @property {number} jdwpPort
|
||||
* @property {number} processId
|
||||
* @property {string} targetDevice
|
||||
* @property {boolean} trace
|
||||
@@ -340,7 +343,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
this.trace = args.trace;
|
||||
onMessagePrint(this.LOG.bind(this));
|
||||
}
|
||||
D(`Attach: ${JSON.stringify(args)}`);
|
||||
D(JSON.stringify({type: 'attach', args, env:process.env}, null, ' '));
|
||||
|
||||
if (args.targetDevice === 'null') {
|
||||
// "null" is returned from the device picker if there's an error or if the
|
||||
@@ -368,6 +371,18 @@ class AndroidDebugSession extends DebugSession {
|
||||
return;
|
||||
}
|
||||
|
||||
// set the custom ADB host and port
|
||||
if (typeof args.adbSocket === 'string' && args.adbSocket) {
|
||||
ADBSocket.HostPort = args.adbSocket;
|
||||
} else if (typeof args.adbPort === 'number' && args.adbPort >= 0 && args.adbPort <= 65535) {
|
||||
ADBSocket.HostPort = `:${args.adbPort}`;
|
||||
}
|
||||
|
||||
// set the fixed JDWP port number (if any)
|
||||
if (typeof args.jdwpPort === 'number' && args.jdwpPort >= 0 && args.jdwpPort <= 65535) {
|
||||
Debugger.portManager.fixedport = args.jdwpPort;
|
||||
}
|
||||
|
||||
try {
|
||||
// app_src_root must end in a path-separator for correct validation of sub-paths
|
||||
this.app_src_root = ensure_path_end_slash(args.appSrcRoot);
|
||||
@@ -446,10 +461,10 @@ class AndroidDebugSession extends DebugSession {
|
||||
if (/^ADB server is not running/.test(e.msg)) {
|
||||
this.LOG('Make sure the Android SDK Platform Tools are installed and run:');
|
||||
this.LOG(' adb start-server');
|
||||
this.LOG('If you are running ADB on a non-default port, also make sure the adbPort value in your launch.json is correct.');
|
||||
this.LOG('If you are running ADB using a non-default configuration, also make sure the adbSocket value in your launch.json is correct.');
|
||||
}
|
||||
if (/ADB|JDWP/.test(msg)) {
|
||||
this.LOG('Ensure any instances of Android Studio are closed.');
|
||||
this.LOG('Ensure any instances of Android Studio are closed and ADB is running.');
|
||||
}
|
||||
// tell the client we're done
|
||||
this.terminate_reason = `start-exception: ${msg}`;
|
||||
@@ -460,11 +475,13 @@ class AndroidDebugSession extends DebugSession {
|
||||
/**
|
||||
* @typedef AndroidLaunchArguments
|
||||
* @property {number} adbPort
|
||||
* @property {string} adbSocket
|
||||
* @property {string[]} amStartArgs
|
||||
* @property {string} apkFile
|
||||
* @property {string} appSrcRoot
|
||||
* @property {boolean} autoStartADB
|
||||
* @property {number} callStackDisplaySize
|
||||
* @property {number} jdwpPort
|
||||
* @property {string} launchActivity
|
||||
* @property {string} manifestFile
|
||||
* @property {string[]} pmInstallArgs
|
||||
@@ -484,7 +501,7 @@ class AndroidDebugSession extends DebugSession {
|
||||
this.trace = args.trace;
|
||||
onMessagePrint(this.LOG.bind(this));
|
||||
}
|
||||
D(`Launch: ${JSON.stringify(args)}`);
|
||||
D(JSON.stringify({type: 'launch', args, env:process.env}, null, ' '));
|
||||
|
||||
if (args.targetDevice === 'null') {
|
||||
// "null" is returned from the device picker if there's an error or if the
|
||||
@@ -512,9 +529,16 @@ class AndroidDebugSession extends DebugSession {
|
||||
return;
|
||||
}
|
||||
|
||||
// set the custom ADB port - this should be changed to pass it to each ADBClient instance
|
||||
if (typeof args.adbPort === 'number' && args.adbPort >= 0 && args.adbPort <= 65535) {
|
||||
ADBSocket.ADBPort = args.adbPort;
|
||||
// set the custom ADB host and port
|
||||
if (typeof args.adbSocket === 'string' && args.adbSocket) {
|
||||
ADBSocket.HostPort = args.adbSocket;
|
||||
} else if (typeof args.adbPort === 'number' && args.adbPort >= 0 && args.adbPort <= 65535) {
|
||||
ADBSocket.HostPort = `:${args.adbPort}`;
|
||||
}
|
||||
|
||||
// set the fixed JDWP port number (if any)
|
||||
if (typeof args.jdwpPort === 'number' && args.jdwpPort >= 0 && args.jdwpPort <= 65535) {
|
||||
Debugger.portManager.fixedport = args.jdwpPort;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -52,9 +52,14 @@ class Debugger extends EventEmitter {
|
||||
|
||||
static portManager = {
|
||||
portrange: { lowest: 31000, highest: 31099 },
|
||||
fixedport: 0,
|
||||
inuseports: new Set(),
|
||||
debuggers: {},
|
||||
reserveport: function () {
|
||||
if (this.fixedport > 0 && this.fixedport < 65536) {
|
||||
this.inuseports.add(this.fixedport);
|
||||
return this.fixedport;
|
||||
}
|
||||
// choose a random port to use each time
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
const portidx = this.portrange.lowest + ((Math.random() * 100) | 0);
|
||||
|
||||
@@ -7,10 +7,17 @@ const AndroidSocket = require('./androidsocket');
|
||||
class ADBSocket extends AndroidSocket {
|
||||
|
||||
/**
|
||||
* The port number to run ADB on.
|
||||
* The value can be overriden by the adbPort value in each configuration.
|
||||
* The host and port number to run ADB commands on, in 'host:port' format (host part is optional).
|
||||
* The value can be overriden by the adbSocket (or the deprecated adbPort) value in each debug configuration.
|
||||
*
|
||||
* The default host value is left blank as this is the simplest way to
|
||||
* specify "connect to the local machine" without explicitly specifying
|
||||
* 'localhost' or '127.0.0.1' (which may be mapped to something else)
|
||||
*/
|
||||
static ADBPort = 5037;
|
||||
static HostPort = `:5037`;
|
||||
static get DefaultHostPort() {
|
||||
return `:5037`
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super('ADBSocket');
|
||||
@@ -18,13 +25,14 @@ class ADBSocket extends AndroidSocket {
|
||||
|
||||
/**
|
||||
* Reads and checks the reply from an ADB command
|
||||
* @param {string} command
|
||||
* @param {boolean} [throw_on_fail] true if the function should throw on non-OKAY status
|
||||
*/
|
||||
async read_adb_status(throw_on_fail = true) {
|
||||
async read_adb_status(command, throw_on_fail = true) {
|
||||
// read back the status
|
||||
const status = await this.read_bytes(4, 'latin1')
|
||||
if (status !== 'OKAY' && throw_on_fail) {
|
||||
throw new Error(`ADB command failed. Status: '${status}'`);
|
||||
throw new Error(`ADB command '${command}' failed. Status: '${status}'`);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -57,7 +65,7 @@ class ADBSocket extends AndroidSocket {
|
||||
*/
|
||||
async cmd_and_status(command) {
|
||||
await this.write_adb_command(command);
|
||||
return this.read_adb_status();
|
||||
return this.read_adb_status(command);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +112,7 @@ class ADBSocket extends AndroidSocket {
|
||||
await this.write_bytes(done_and_mtime);
|
||||
|
||||
// read the final status and any error message
|
||||
const result = await this.read_adb_status(false);
|
||||
const result = await this.read_adb_status('sync:', false);
|
||||
const failmsg = await this.read_le_length_data('latin1');
|
||||
|
||||
// finish the transfer mode
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const { ADBClient } = require('../adbclient');
|
||||
const ADBSocket = require('../sockets/adbsocket');
|
||||
const { ADBClient, getADBSocketParams } = require('../adbclient');
|
||||
const { LOG } = require('../utils/print');
|
||||
|
||||
function getAndroidSDKFolder() {
|
||||
@@ -41,19 +40,21 @@ function getADBPathName() {
|
||||
return path.join(android_sdk, 'platform-tools', /^win/.test(process.platform)?'adb.exe':'adb');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} port
|
||||
*/
|
||||
function startADBServer(port) {
|
||||
if (typeof port !== 'number' || port <= 0 || port >= 65536) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function startADBServer() {
|
||||
const adb_exe_path = getADBPathName();
|
||||
if (!adb_exe_path) {
|
||||
return false;
|
||||
}
|
||||
const adb_start_server_args = ['-P',`${port}`,'start-server'];
|
||||
const adb_socket = getADBSocketParams();
|
||||
// don't try and start ADB if the server is on a remote host
|
||||
if (!/^(localhost|127\.\d+\.\d+\.\d+)?$/.test(adb_socket.host)) {
|
||||
LOG(`Cannot launch adb server on remote host ${adb_socket.host}:${adb_socket.port}`);
|
||||
return;
|
||||
}
|
||||
const adb_start_server_args = ['-P',`${adb_socket.port}`,'start-server'];
|
||||
if (adb_socket.host) {
|
||||
adb_start_server_args.unshift(`-H`, adb_socket.host);
|
||||
}
|
||||
try {
|
||||
LOG([adb_exe_path, ...adb_start_server_args].join(' '));
|
||||
const stdout = require('child_process').execFileSync(adb_exe_path, adb_start_server_args, {
|
||||
@@ -73,7 +74,7 @@ async function checkADBStarted(auto_start) {
|
||||
const err = await new ADBClient().test_adb_connection();
|
||||
// if adb is not running, see if we can start it ourselves using ANDROID_HOME (and a sensible port number)
|
||||
if (err && auto_start) {
|
||||
return startADBServer(ADBSocket.ADBPort);
|
||||
return startADBServer();
|
||||
}
|
||||
return !err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user