@u4/adbkit
Version:
A Typescript client for the Android Debug Bridge.
183 lines • 6.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LateTransportError = exports.PrematurePacketError = void 0;
const events_1 = __importDefault(require("events"));
const packet_1 = __importDefault(require("./packet"));
const protocol_1 = __importDefault(require("../protocol"));
const utils_1 = __importDefault(require("../utils"));
const debug = utils_1.default.debug('adb:tcpusb:service');
class PrematurePacketError extends Error {
constructor(packet) {
super();
this.packet = packet;
Object.setPrototypeOf(this, PrematurePacketError.prototype);
this.name = 'PrematurePacketError';
this.message = 'Premature packet';
Error.captureStackTrace(this, Service.PrematurePacketError);
}
}
exports.PrematurePacketError = PrematurePacketError;
class LateTransportError extends Error {
constructor() {
super();
Object.setPrototypeOf(this, LateTransportError.prototype);
this.name = 'LateTransportError';
this.message = 'Late transport';
Error.captureStackTrace(this, Service.LateTransportError);
}
}
exports.LateTransportError = LateTransportError;
class Service extends events_1.default {
constructor(client, serial, localId, remoteId, socket) {
super();
this.client = client;
this.serial = serial;
this.localId = localId;
this.remoteId = remoteId;
this.socket = socket;
this.opened = false;
this.ended = false;
this.needAck = false;
this.on = (event, listener) => super.on(event, listener);
this.off = (event, listener) => super.off(event, listener);
this.once = (event, listener) => super.once(event, listener);
this.emit = (event, ...args) => super.emit(event, ...args);
}
end() {
if (this.transport) {
this.transport.end();
}
if (this.ended) {
return this;
}
debug('O:A_CLSE');
const localId = this.opened ? this.localId : 0; // Zero can only mean a failed open
try {
// We may or may not have gotten here due to @socket ending, so write
// may fail.
this.socket.write(packet_1.default.assemble(packet_1.default.A_CLSE, localId, this.remoteId));
}
catch (error) {
// ignore error
}
// Let it go
this.transport = undefined;
this.ended = true;
this.emit('end');
return this;
}
async handle(packet) {
try {
switch (packet.command) {
case packet_1.default.A_OPEN:
this._handleOpenPacket(packet);
break;
case packet_1.default.A_OKAY:
this._handleOkayPacket(packet);
break;
case packet_1.default.A_WRTE:
this._handleWritePacket(packet);
break;
case packet_1.default.A_CLSE:
this._handleClosePacket(packet);
break;
default:
throw new Error(`Unexpected packet ${packet.command}`);
}
return true;
}
catch (err) {
this.emit('error', err);
this.end();
return false;
}
}
async _handleOpenPacket(packet) {
debug('I:A_OPEN', packet);
try {
const transport = await this.client.getDevice(this.serial).transport();
this.transport = transport;
if (this.ended) {
throw new LateTransportError();
}
if (!packet.data)
throw Error("missing data in packet");
this.transport.write(protocol_1.default.encodeData(packet.data.subarray(0, -1))); // Discard null byte at end
await this.transport.parser.readCode(protocol_1.default.OKAY);
debug('O:A_OKAY');
this.socket.write(packet_1.default.assemble(packet_1.default.A_OKAY, this.localId, this.remoteId));
this.opened = true;
return new Promise((resolve, reject) => {
if (!this.transport) {
return reject('transport is closed');
}
this.transport.socket
.on('readable', () => this._tryPush())
.on('end', resolve)
.on('error', reject);
this._tryPush();
});
}
finally {
this.end();
}
}
_handleOkayPacket(packet) {
debug('I:A_OKAY', packet);
if (this.ended) {
return false;
}
if (!this.transport) {
throw new Service.PrematurePacketError(packet);
}
this.needAck = false;
return this._tryPush();
}
_handleWritePacket(packet) {
debug('I:A_WRTE', packet);
if (this.ended) {
return false;
}
if (!this.transport) {
throw new Service.PrematurePacketError(packet);
}
if (this.transport && packet.data) {
this.transport.write(packet.data);
}
debug('O:A_OKAY');
return this.socket.write(packet_1.default.assemble(packet_1.default.A_OKAY, this.localId, this.remoteId));
}
_handleClosePacket(packet) {
debug('I:A_CLSE', packet);
if (this.ended) {
return false;
}
if (!this.transport) {
throw new Service.PrematurePacketError(packet);
}
this.end();
return true;
}
_tryPush() {
if (this.needAck || this.ended || !this.transport) {
return false;
}
const chunk = this._readChunk(this.transport.socket);
if (chunk) {
debug('O:A_WRTE');
this.socket.write(packet_1.default.assemble(packet_1.default.A_WRTE, this.localId, this.remoteId, chunk));
return (this.needAck = true);
}
return false;
}
_readChunk(stream) {
return (stream.read(this.socket.maxPayload) || stream.read());
}
}
Service.PrematurePacketError = PrematurePacketError;
Service.LateTransportError = LateTransportError;
exports.default = Service;
//# sourceMappingURL=service.js.map