1let adb_ws;
2let logcat = document.getElementById('logcat');
3
4let utf8Encoder = new TextEncoder();
5let utf8Decoder = new TextDecoder();
6
7const A_CNXN = 0x4e584e43;
8const A_OPEN = 0x4e45504f;
9const A_WRTE = 0x45545257;
10const A_OKAY = 0x59414b4f;
11
12const kLocalChannelId = 666;
13
14function setU32LE(array, offset, x) {
15    array[offset] = x & 0xff;
16    array[offset + 1] = (x >> 8) & 0xff;
17    array[offset + 2] = (x >> 16) & 0xff;
18    array[offset + 3] = x >> 24;
19}
20
21function getU32LE(array, offset) {
22    let x = array[offset]
23        | (array[offset + 1] << 8)
24        | (array[offset + 2] << 16)
25        | (array[offset + 3] << 24);
26
27    return x >>> 0;  // convert signed to unsigned if necessary.
28}
29
30function computeChecksum(array) {
31    let sum = 0;
32    let i;
33    for (i = 0; i < array.length; ++i) {
34        sum = ((sum + array[i]) & 0xffffffff) >>> 0;
35    }
36
37    return sum;
38}
39
40function createAdbMessage(command, arg0, arg1, payload) {
41    let arrayBuffer = new ArrayBuffer(24 + payload.length);
42    let array = new Uint8Array(arrayBuffer);
43    setU32LE(array, 0, command);
44    setU32LE(array, 4, arg0);
45    setU32LE(array, 8, arg1);
46    setU32LE(array, 12, payload.length);
47    setU32LE(array, 16, computeChecksum(payload));
48    setU32LE(array, 20, command ^ 0xffffffff);
49    array.set(payload, 24);
50
51    return arrayBuffer;
52}
53
54function adbOpenConnection() {
55    let systemIdentity = utf8Encoder.encode("Cray_II:1234:whatever");
56
57    let arrayBuffer = createAdbMessage(
58        A_CNXN, 0x1000000, 256 * 1024, systemIdentity);
59
60    adb_ws.send(arrayBuffer);
61}
62
63function adbOpenChannel() {
64    let destination = utf8Encoder.encode("shell:logcat");
65
66    let arrayBuffer = createAdbMessage(A_OPEN, kLocalChannelId, 0, destination);
67    adb_ws.send(arrayBuffer);
68}
69
70function adbSendOkay(remoteId) {
71    let payload = new Uint8Array(0);
72
73    let arrayBuffer = createAdbMessage(
74        A_OKAY, kLocalChannelId, remoteId, payload);
75
76    adb_ws.send(arrayBuffer);
77}
78
79function adbOnMessage(ev) {
80    // console.log("adb_ws: onmessage (" + ev.data.byteLength + " bytes)");
81
82    let arrayBuffer = ev.data;
83    let array = new Uint8Array(arrayBuffer);
84
85    if (array.length < 24) {
86        console.log("adb message too short.");
87        return;
88    }
89
90    let command = getU32LE(array, 0);
91    let magic = getU32LE(array, 20);
92
93    if (command != ((magic ^ 0xffffffff) >>> 0)) {
94        console.log("command = " + command + ", magic = " + magic);
95        console.log("adb message command vs magic failed.");
96        return;
97    }
98
99    let payloadLength = getU32LE(array, 12);
100
101    if (array.length != 24 + payloadLength) {
102        console.log("adb message length mismatch.");
103        return;
104    }
105
106    let payloadChecksum = getU32LE(array, 16);
107    let checksum = computeChecksum(array.slice(24));
108
109    if (payloadChecksum != checksum) {
110        console.log("adb message checksum mismatch.");
111        return;
112    }
113
114    switch (command) {
115        case A_CNXN:
116        {
117            console.log("connected.");
118
119            adbOpenChannel();
120            break;
121        }
122
123        case A_OKAY:
124        {
125            let remoteId = getU32LE(array, 4);
126            console.log("channel created w/ remoteId " + remoteId);
127            break;
128        }
129
130        case A_WRTE:
131        {
132            let payloadText = utf8Decoder.decode(array.slice(24));
133
134            logcat.value += payloadText;
135
136            // Scroll to bottom
137            logcat.scrollTop = logcat.scrollHeight;
138
139            let remoteId = getU32LE(array, 4);
140            adbSendOkay(remoteId);
141            break;
142        }
143    }
144}
145
146function init_logcat() {
147    const wsProtocol = (location.protocol == "http:") ? "ws:" : "wss:";
148
149    adb_ws = new WebSocket(
150        wsProtocol + "//" + location.host + "/control_adb");
151
152    adb_ws.binaryType = "arraybuffer";
153
154    adb_ws.onopen = function() {
155        console.log("adb_ws: onopen");
156
157        adbOpenConnection();
158
159        logcat.style.display = "initial";
160    };
161    adb_ws.onmessage = adbOnMessage;
162}
163
164