UNPKG

@u4/adbkit

Version:

A Typescript client for the Android Debug Bridge.

183 lines 6.5 kB
"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