1*c4ef02aeSEvan Bacon/**
2*c4ef02aeSEvan Bacon * Copyright (c) 2021 Expo, Inc.
3*c4ef02aeSEvan Bacon * Copyright (c) 2018 Drifty Co.
4*c4ef02aeSEvan Bacon *
5*c4ef02aeSEvan Bacon * This source code is licensed under the MIT license found in the
6*c4ef02aeSEvan Bacon * LICENSE file in the root directory of this source tree.
7*c4ef02aeSEvan Bacon */
8*c4ef02aeSEvan Bacon
9*c4ef02aeSEvan Baconimport plist from '@expo/plist';
10*c4ef02aeSEvan Baconimport Debug from 'debug';
11*c4ef02aeSEvan Baconimport { Socket } from 'net';
12*c4ef02aeSEvan Bacon
13*c4ef02aeSEvan Baconimport type { ProtocolWriter } from './AbstractProtocol';
14*c4ef02aeSEvan Baconimport { PlistProtocolReader, ProtocolClient, ProtocolReaderFactory } from './AbstractProtocol';
15*c4ef02aeSEvan Bacon
16*c4ef02aeSEvan Baconconst debug = Debug('expo:apple-device:protocol:usbmux');
17*c4ef02aeSEvan Bacon
18*c4ef02aeSEvan Baconexport const USBMUXD_HEADER_SIZE = 16;
19*c4ef02aeSEvan Bacon
20*c4ef02aeSEvan Baconexport interface UsbmuxMessage {
21*c4ef02aeSEvan Bacon  messageType: string;
22*c4ef02aeSEvan Bacon  extraFields?: { [key: string]: any };
23*c4ef02aeSEvan Bacon}
24*c4ef02aeSEvan Bacon
25*c4ef02aeSEvan Baconexport class UsbmuxProtocolClient extends ProtocolClient<UsbmuxMessage> {
26*c4ef02aeSEvan Bacon  constructor(socket: Socket) {
27*c4ef02aeSEvan Bacon    super(socket, new ProtocolReaderFactory(UsbmuxProtocolReader), new UsbmuxProtocolWriter());
28*c4ef02aeSEvan Bacon  }
29*c4ef02aeSEvan Bacon}
30*c4ef02aeSEvan Bacon
31*c4ef02aeSEvan Baconexport class UsbmuxProtocolReader extends PlistProtocolReader {
32*c4ef02aeSEvan Bacon  constructor(callback: (data: any) => any) {
33*c4ef02aeSEvan Bacon    super(USBMUXD_HEADER_SIZE, callback);
34*c4ef02aeSEvan Bacon  }
35*c4ef02aeSEvan Bacon
36*c4ef02aeSEvan Bacon  parseHeader(data: Buffer) {
37*c4ef02aeSEvan Bacon    return data.readUInt32LE(0) - USBMUXD_HEADER_SIZE;
38*c4ef02aeSEvan Bacon  }
39*c4ef02aeSEvan Bacon
40*c4ef02aeSEvan Bacon  parseBody(data: Buffer) {
41*c4ef02aeSEvan Bacon    const resp = super.parseBody(data);
42*c4ef02aeSEvan Bacon    debug(`Response: ${JSON.stringify(resp)}`);
43*c4ef02aeSEvan Bacon    return resp;
44*c4ef02aeSEvan Bacon  }
45*c4ef02aeSEvan Bacon}
46*c4ef02aeSEvan Bacon
47*c4ef02aeSEvan Baconexport class UsbmuxProtocolWriter implements ProtocolWriter {
48*c4ef02aeSEvan Bacon  private useTag = 0;
49*c4ef02aeSEvan Bacon
50*c4ef02aeSEvan Bacon  write(socket: Socket, msg: UsbmuxMessage) {
51*c4ef02aeSEvan Bacon    // TODO Usbmux message type
52*c4ef02aeSEvan Bacon    debug(`socket write: ${JSON.stringify(msg)}`);
53*c4ef02aeSEvan Bacon    const { messageType, extraFields } = msg;
54*c4ef02aeSEvan Bacon    const plistMessage = plist.build({
55*c4ef02aeSEvan Bacon      BundleID: 'dev.expo.native-run', // TODO
56*c4ef02aeSEvan Bacon      ClientVersionString: 'usbmux.js', // TODO
57*c4ef02aeSEvan Bacon      MessageType: messageType,
58*c4ef02aeSEvan Bacon      ProgName: 'native-run', // TODO
59*c4ef02aeSEvan Bacon      kLibUSBMuxVersion: 3,
60*c4ef02aeSEvan Bacon      ...extraFields,
61*c4ef02aeSEvan Bacon    });
62*c4ef02aeSEvan Bacon
63*c4ef02aeSEvan Bacon    const dataSize = plistMessage ? plistMessage.length : 0;
64*c4ef02aeSEvan Bacon    const protocolVersion = 1;
65*c4ef02aeSEvan Bacon    const messageCode = 8;
66*c4ef02aeSEvan Bacon
67*c4ef02aeSEvan Bacon    const header = Buffer.alloc(USBMUXD_HEADER_SIZE);
68*c4ef02aeSEvan Bacon    header.writeUInt32LE(USBMUXD_HEADER_SIZE + dataSize, 0);
69*c4ef02aeSEvan Bacon    header.writeUInt32LE(protocolVersion, 4);
70*c4ef02aeSEvan Bacon    header.writeUInt32LE(messageCode, 8);
71*c4ef02aeSEvan Bacon    header.writeUInt32LE(this.useTag++, 12); // TODO
72*c4ef02aeSEvan Bacon    socket.write(header);
73*c4ef02aeSEvan Bacon    socket.write(plistMessage);
74*c4ef02aeSEvan Bacon  }
75*c4ef02aeSEvan Bacon}
76